2011-09-15 5 views
2

Je travaille sur la solution pour this koan, qui stipule:En utilisant une méthode définie dans une autre méthode

Write a function which replicates each element of a sequence a variable number of times. 

Pour ce faire, je veux:

  1. Faire une méthode qui prend une séquence et le nombre de fois pour répéter chaque élément.
  2. Définir une méthode locale dans cette méthode qui duplique une valeur, v, n fois dans une séquence.

Dans cet esprit, j'ai écrit la méthode suivante:

(fn dupSeqX [aseq x] 
    (fn dupx [v x] 
    (if (= x 1) (list v) (concat v (dupx v (- x 1)))) 
) 
    (reverse (reduce #(concat %1 (dupx %2 x)) '() aseq))) 

Lors de l'exécution de ce code, je reçois l'erreur suivante:

java.security.PrivilegedActionException: java.lang.Exception: Unable to resolve symbol: dupx in this context (NO_SOURCE_FILE:0) 

Comment puis-je faire pour créer une locale méthode qui me permettra de finir ce koan?

Y a-t-il une façon «clojure-esque» de le faire que je ne connais pas?

+2

Voici quelques conseils. Je pense que vous voulez dire la fonction au lieu de la méthode. De plus, les fonctions sont définies avec defn mais les fonctions anonymes (lamdba) sont définies avec fn. Vous confondez les deux. En outre, si vous avez besoin d'une fonction locale dans la portée de votre fonction externe, utilisez let ou letfn. –

Répondre

8

Tout d'abord, nous ne parlons pas de méthodes: on parle de fonctions. Il y a quelque chose dans clojure que vous pourriez appeler une méthode mais c'est différent d'une fonction. Si vous arrêtez d'utiliser le jargon OO, vous perdrez également le style de pensée OO.

Ce que vous essayez de faire est possible. Vous souhaitez fondamentalement créer une nouvelle fonction avec le nom dupx dans la fonction dupseqx. Ce que vous faites en ce moment est de créer une fonction et ensuite de la jeter (vous ne faites rien avec la valeur de retour et seulement le dernier formulaire d'une fonction est renvoyé). Comme une fonction est comme n'importe quelle autre valeur, vous pouvez utiliser le même mécanisme qu'avec n'importe quelle autre valeur: créer une "variable" locale. Quel est le mécanisme pour cela? Il est une liaison locale et cela fonctionne comme celui-ci (le nom dans la fn est juste pour que vous pouvez appeler à partir de lui-même, il n'a pas besoin d'être le même que le nom let liØe):

(let [dupx (fn dupx [v x] 
      (if (= x 1) 
        (list v) 
        (cons v (dupx v (dec x)))))] 
    (dupx 5 3)) 

Avis que j'ai corrigé d'autres choses.

Une courte forme de cette (fixation double nom laideur):

(letfn [(dupx [v x] (if (= x 1) 
         (list v) 
         (cons v (dupx v (dec x)))))] 
    (dupx 5 3)) 

Ok dans tout entre « (nous allons [...] » Et la mise en correspondance ")" nous avons maintenant une fonction dupx

Alors maintenant, le reste de votre code fonctionne.

(fn dupSeqX [aseq x] 
    (letfn [(dupx [v x] (if (= x 1) (list v) (cons v (dupx v (dec x)))))] 
    (reverse (reduce #(concat %1 (dupx %2 x)) '() aseq)))) 

Ce code peut être un peu plus idiomatiques:

  • Directives de codage: paramètres de nom coll au lieu de aseq
  • Directives de codage: DoNotUseCamalCase do-it-like-ce
  • Récursion lorsque vous n'en avez pas besoin est mauvais pour la performance et les grands nombres.
  • Vous réinventez la roue. C'est bon d'apprendre le codage mais pas bon si vous voulez apprendre à connaître la langue et la lib standard.

Comment est-ce que j'ai écrit ceci?

D'abord le fn de base. coll est la norme pour la fonction de nommage qui attendent des séquences.

(fn [coll times] ) 

Si vous lisez ceci "chaque élément d'une séquence" votre cerveau doit aller MAP. "Réplique chaque ..." est essentiellement une description de ce que vous devez mettre dans la fonction de la carte. Nous pouvons utiliser repeat (votre fonction dubx mais avec quelques goodies supplémentaires comme ça c'est paresseux).

(fn [coll times] 
    (map (fn [val] (repeat times val)) coll)) 

Il reste un problème (provenant du koan). Il veut un seq en arrière, pas une séquence dans une séquence pour chaque élément. Cela signifie que nous devons concaténer le résultat ensemble.

(fn [coll times] 
    (apply concat (map (fn [val] (repeat times val)) coll))) 

Vous verrez souvent le modèle (apply concat (map ....)). Il y a une meilleure fonction pour cela dans la bibliothèque standard, appelée mapcat, et je vais faire la fonction interne dans la syntaxe courte.

(fn [coll times] 
    (mapcat #(repeat times %) coll)) 

Hope that helps!

+0

C'est vraiment une super description, merci! J'apprécie votre perspicacité. – Davidann

2

Clojure a beaucoup ou de très bonnes fonctions pour vous aider à entrer dans l'habbit de "penser en seqs". Quand vous trouvez votre auto écrit quelque chose qui itère à travers une liste pensez "puis-je cartographier ceci?", Et quand vous trouvez que vous faites tout autre chose à une liste, regardez ce list of useful seq functions.

dans ce cas que la fonction interne arrive à être intégrée afin que vous ayez de la chance :) souvent vous aurez besoin de l'écrire vous-même et de le stocker dans un let si vous le rendre une fonction appropriée, vous pouvez trouver ailleurs dans votre code.

Heres un soupçon pour commencer

(flatten (map #(repeat 3 %) [1 2 3 4])) 
(1 1 1 2 2 2 3 3 3 4 4 4) 

ps: #(repeat 3 %) est un raccourci pour (fn [n] (repeat 3 n))

+0

'(aplatir (map ...))' -> '(mapcat ...)' (que se passe-t-il si certains éléments sont susceptibles de s'aplatir?). Félicitations pour 10k. :-) –

+0

oui, (carte mapcat arthur). :) aplatir les cartes est quelque chose d'une mauvaise habitude –

Questions connexes