2017-07-20 2 views
1

Si j'ai la chaîne suivante contenant une forme valide Clojure/ClojureScript:Comment évaluer la première étape d'une forme Clojure?

"(+ 1 (+ 2 (/ 6 3)))"

Comment pourrais-je évaluer la première « étape » de cette forme? En d'autres termes, comment pourrais-je tourner le formulaire ci-dessus en ceci:

"(+ 1 (+ 2 2))"

puis tourner cette forme correspondant à ceci:

"(+ 1 4)"

Répondre

3

Vous utilisez récursivité.

Vous devez avoir une fonction qui permet d'évaluer les nombres eux-mêmes, mais si ce n'est pas un numéro que vous devez appliquer l'opération sur l'évaluation des arguments .. Ainsi

(evaluate '(+ 1 (+ 2 (/ 6 3)))) 

Cela devrait être traité comme: Lorsque vous commencez à faire votre première étape, plusieurs étapes attendent que les résultats soient également effectués.

(+ (evaluate '1) (evaluate '(+ 2 (/ 6 3)))) 

Remarque J'utilise une structure de liste et non des chaînes. Avec les chaînes, vous devrez utiliser une fonction pour l'analyser.

+0

Quel est mon cas de base? Comment puis-je déterminer que (/ 6 3) doit être évalué en premier? –

+1

Votre scénario de référence est une expression numérique. '6' évalue à' 6'. Ainsi '(évaluer '(/ 6 3)); ==> ((get-fun '/) (évaluer' 6) (évaluer '3)) '. Vous n'avez pas besoin de le déterminer puisque l'évaluateur fera toujours ses dépendances en premier. – Sylwester

1

Ci-dessous est une implémentation très basique qui fait ce que vous cherchez. Il serait plus commune à eval la forme entière, mais puisque vous êtes désireux de simplement simplifier les expressions les plus intimes, ce le fait:

(defn leaf? 
    [x] 
    (and (list? x) 
     (symbol? (first x)) 
     (not-any? list? (rest x)))) 

(defn eval-one 
    [expr] 
    (cond 
    (leaf? expr) (apply (-> (first expr) resolve var-get) 
         (rest expr)) 
    (list? expr) (apply list (map eval-one expr)) 
    :default expr 
    )) 


(read-string "(+ 1 (+ 2 (/ 6 3)))") 
=> (+ 1 (+ 2 (/ 6 3))) 
(eval-one *1) 
=> (+ 1 (+ 2 2)) 
(eval-one *1) 
=> (+ 1 4) 
(eval-one *1) 
=> 5 

C'est naïf et à des fins d'illustration seulement, donc s'il vous plaît ne soyez pas sous l'impression qu'un vrai eval fonctionnerait de cette façon.

Nous définissons une feuille comme une liste dont le premier élément est un symbole et qui ne contient aucune autre liste qui pourrait être évaluée. Nous traitons ensuite le formulaire, évaluant les expressions feuilles, évaluant récursivement les expressions non-feuilles qui sont des listes, et pour toute autre chose, nous l'insérons simplement dans l'expression résultante. Le résultat est que toutes les expressions les plus profondes qui peuvent être évaluées, selon notre définition, sont évaluées.

2

Les autres réponses sont excellentes si vous voulez exécuter du code par étapes, mais je tiens à mentionner que cette évaluation peut également être visualisée en utilisant un débogueur. Voir ci-dessous Cider's debugger en action:

Visualization of debugging

En utilisant cider-debug-defun-at-point on ajoute un point d'arrêt sur evaluate. Ensuite, lorsque la définition evaluate est évaluée, le point d'arrêt est atteint et nous parcourons le code en appuyant plusieurs fois sur next. Un débogueur est très pratique lorsque vous voulez évaluer les «étapes» des formulaires.

+0

Oh, je n'y ai même pas pensé, mais un débogueur est exactement ce que je cherche. –

+0

Je ne semble pas avoir cider-debug-defun-at-point dans mon installation emacs. Est-ce nouveau? –

+0

@LincolnBergeson Avez-vous ajouté Cider à votre configuration? C'est [inclus] (https://github.com/clojure-emacs/cider/blob/master/cider-debug.el#L743). J'utilise Spacemacs avec la [couche de Clojure] (https://github.com/syl20bnr/spacemacs/tree/master/layers/%2Blang/clojure) ajouté. De cette façon, je devais seulement ajouter 'clojure' à la liste' dotspacemacs-configuration-layers'. –

0

Pour ajouter aux autres grandes réponses, voici une fonction simple qui devrait revenir la première forme d'évaluer dans une chaîne:

(defn first-eval [form-str] 
    (let [form (read-string form-str) 
     tree-s (tree-seq sequential? identity form)] 
    (first (filter #(= % (flatten %)) tree-s)))) 

Utilisation:

(first-eval "(+ 1 (+ 2 (/ 6 3)))") ;; returns (/ 6 3) 

tree-seq est assez limitée dans sa capacité à évaluer TOUS les formulaires, mais c'est un début.