2010-08-16 18 views
10

Je l'ai entendu appelé stream, comme infinite list, et parfois même comme lazy sequence.Quel est le terme correct pour le modèle de programmation fonctionnelle suivant?

Quel est le terme correct pour le modèle suivant? (Code Clojure illustré)

(def first$ first) 

(defn second$ [str] 
    (cond 
    (empty? str)() 
    true ((first (rest str))))) 

(defn stream-builder [next_ n] 
    (cons n (cons (fn [] (stream-builder next_ (next_ n)))()))) 

(defn stream [str n] 
    (cond 
    (= 0 n)() 
    true (cons (first$ str) (stream (second$ str) (- n 1))))) 

(def odd 
    (stream-builder (fn [n] 
     (+ 2 n))1)) 

(println (stream odd 23)) 

> (1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45) 
+1

Je ne suis pas sûr de ce que vous demandez. On dirait que vous avez déjà trouvé 3 noms pour cela. Qu'est-ce qui, selon vous, rendrait une réponse plus "correcte" que n'importe quel autre de ces synonymes? – Ken

Répondre

14

Réponse courte: stream-builder retourne une fonction qui renvoie une séquence/liste infinie, qui doit être évalué « paresseusement » (puisque vous ne pouvez pas évaluer quelque chose d'infiniment longue dans le temps fini). Dans le monde de Clojure, vous ne devriez probablement appeler aucune des choses dans vos exemples de «flux» pour éviter la confusion avec un autre concept.

réponse plus longue:

Un effet secondaire malheureux de la diversité de la pensée dans les langages de programmation est que nous utilisons souvent les mêmes mots pour des significations différentes. Les trois mots que vous avez mentionnés ("Stream", "liste infinie", "séquence paresseuse") se réfèrent à des éléments de traitement en série, et dans Clojure nous appelons ces "Séquences", mais les nuances impliquées par chacun sont légèrement différentes

un « flux » fait généralement référence à une séquence d'éléments, et est aujourd'hui souvent utilisé dans le contexte des séquences de caractères finis. souvent, ces séquences de caractères proviennent d'un fichier, source de réseau ou d'une pipe Unix.

Si une La séquence est définie de telle sorte qu'elle a un nombre infini d'éléments, nous pouvons l'appeler une séquence infinie, généralement des séquences infinies sont représentées en interne comme linked list, donc nous pouvons appeler ces "listes infinies". préfèrent entendre le terme "séquence infinie" dans la communauté Clojure donc nous ne sommes pas liés à une mise en œuvre particulière. Finalement, la nuance de "séquence paresseuse" dans Clojure se réfère à un modèle d'évaluation séquentielle sur une structure de données qui se produit "sur demande". En d'autres termes, l'accent est mis ici sur la nature de l'évaluation paresseux; la valeur d'un élément particulier dans la séquence n'est pas réellement calculée tant que vous ne le demandez pas.

En résumé, Clojure vous devez utiliser les mots:

  • « liste » de se référer à quelque chose avec une liste chaînée mise en œuvre
  • « paresseux » pour désigner les choses qui évaluent la demande
  • séquence « infini » pour faire référence à des séquences qui ne sont pas de taille finie (et doit donc être paresseux)
  • « flux » pour se référer à un tuyau en forme (caractère) à partir d'une source externe
+0

Basé sur la question, je m'attendrais à ce que la réponse courte soit de 1 ou 2 mots ... – Qwertie

10

Ceci n'est pas une réponse à votre question, mais dans l'intérêt de l'écriture de "gentil" code clojure, je voulais souligner quelques points avec votre exemple.

Un des avantages du style fonctionnel est la possibilité de composer des fonctions ensemble. Mais si vous jetez un coup d'œil aux fonctions que vous avez écrites, elles ne font pas grand-chose individuellement, selon les fonctionnalités fournies ailleurs.

Par exemple, stream-builder renvoie simplement une liste de deux éléments de n et une fonction anonyme pour gérer la pseudo-récursivité. Je suppose que vous essayiez d'opter pour une séquence fainéante, mais cela nécessite des fonctions de support qui sont conscientes des détails d'implémentation pour réaliser l'élément suivant, à savoir stream et second$. Heureusement, vous pouvez à la place l'accomplir comme suit:

 
(defn stream-builder [f x] ; f is idiomatic for a function arg 
    (lazy-seq    ; n is idiomatic for a count, so use x instead 
    (cons x 
     (stream-builder f (f x))))) 

Le retourneront au-dessus d'une suite infinie de valeurs, nous avons donc besoin de tirer le comportement limite qui est empaqueté dans stream:

 
(defn limit [n coll] ; coll is idiomatic for a collection arg 
    (lazy-seq   ; flipped order, fns that work on seqs usually take the seq last 
    (when (pos? n) 
     (when-let [s (seq coll)] 
     (cons (first s) 
      (limit (dec n) (next s))))))) 

ci-dessus se paresseusement revenir jusqu'à n éléments de coll:

 
user=> (limit 5 (stream-builder inc 1)) 
(1 2 3 4 5) 

À la fin, chaque fonction fait une chose bien, et est composable avec d'autres fonctions:

Enfin, vous avez défini odd comme la séquence paresseuse des entiers impairs. Le danger est que cette séquence reste collée au fur et à mesure de sa réalisation (c'est ce qu'on appelle «se tenir sur la tête» d'une séquence). Cela peut prendre inutilement de la mémoire en trop pour tenir sur chaque élément jamais réalisé, et empêche la récupération de place.

Pour résoudre ce problème, soit ne déf pas la séquence paresseux, ou def avec la limite, par exemple:

 
(def odds (limit 23 (stream-builder #(+ 2 %) 1))) 

Pour référence ultérieure, ce que nous venons d'écrire est disponible dans le répertoire lib de base comme iterate et take, respectivement:

 
user=> (take 5 (iterate inc 1)) 
(1 2 3 4 5) 
+0

+1 pour la belle décomposition de la façon d'écrire Clojure idiomatique! –