2010-02-18 6 views
3

J'ai écrit plus de code Lisp récemment. En particulier, les fonctions récursives qui prennent des données et construisent une structure de données résultante. Parfois, il semble que je doive transmettre deux ou trois informations à l'invocation suivante de la fonction, en plus des données fournies par l'utilisateur. Appelons ces accumulateurs.Quelles sont les meilleures pratiques pour inclure des paramètres tels qu'un accumulateur dans les fonctions?

Quelle est la meilleure façon d'organiser ces interfaces avec mon code?

Actuellement, je fais quelque chose comme ceci:

(defun foo (user1 user2 &optional acc1 acc2 acc3) 
    ;; do something 
    (foo user1 user2 (cons x acc1) (cons y acc2) (cons z acc3))) 

Cela fonctionne comme je l'aime, mais je suis inquiet parce que je ne ai pas vraiment besoin de présenter les paramètres optionnels & au programmateur .

3 approches que je suis un peu en tenant compte:

  • ont une fonction wrapper qu'un utilisateur est invité à utiliser immédiatement le qui appelle definiton étendu.

  • utiliser labels en interne dans une fonction dont la signature est concise.

  • commencez simplement à utiliser une boucle et des variables. Cependant, je préférerais ne pas le faire puisque je voudrais vraiment envelopper ma tête autour de la récursivité.

Merci les gars!

Répondre

4

Si vous voulez écrire Common Lisp idiomatique, je recommanderais la boucle et les variables pour l'itération. La récursivité est cool, mais ce n'est qu'un outil parmi d'autres pour le Common Lisper. De plus, l'élimination de la queue n'est pas garantie par la spécification Common Lisp. Cela dit, je recommanderais l'approche labels si vous avez une structure, un arbre par exemple, qui est inévitablement récursive et que vous ne pouvez pas recevoir d'appels de queue de toute façon. Les arguments facultatifs permettent aux détails de l'implémentation de s'échapper vers l'appelant.

+0

Merci.J'avais certaines fonctions traitant des arbres, donc je les ai convertis pour utiliser des 'labels'. Un autre ensemble de fonctions fonctionnant uniquement sur des listes, j'ai donc utilisé cette opportunité pour me familiariser avec 'loop'. –

2

Votre impulsion pour protéger les détails d'implémentation de l'utilisateur est intelligente, je pense. Je ne connais pas le Lisp commun, mais dans Scheme vous le faites en définissant votre fonction d'aide dans la portée lexicale de la fonction publique.

(define (fibonacci n) 
    (let fib-accum ((a 0) 
        (b 1) 
        (n n)) 
    (if (< n 1) 
     a 
     (fib-accum b (+ a b) (- n 1))))) 

L'expression let définit une fonction et il se fixe à un nom qui est visible que dans LET, invoque alors la fonction.

+1

Oui, je pense que 'labels 'du Common Lisp est à peu près équivalent au' let' de Scheme dans ce cas. – Ken

0

J'ai utilisé toutes les options que vous mentionnez. Tous ont leurs mérites, donc cela se résume à des préférences personnelles.

Je suis arrivé à utiliser tout ce que je juge approprié. Si je pense que laisser les accumulateurs &optional dans l'API peut avoir un sens pour l'utilisateur, je le laisse. Par exemple, dans une fonction similaire à reduce, l'accumulateur peut être utilisé par l'utilisateur pour fournir une valeur de départ. Sinon, je vais souvent le réécrire en tant que loop, do, ou iter (à partir de la bibliothèque itérative), s'il est logique de le percevoir comme tel. Parfois, l'assistant labels est également utilisé.

Questions connexes