你怎么能在REPL中进行破坏?

时间:2017-10-11 17:27:38

标签: clojure read-eval-print-loop destructuring

假设我有一个函数(remove-bad-nodes g),它返回如下序列:

[updated-g bad-nodes]

其中updated-g是删除了错误节点的图表,bad-nodes是包含已删除节点的集合。

作为函数的参数或let内的参数,我可以像这样解构它:

(let [[g bads] (remove-bad-nodes g)]
  ...)

但这只定义了局部变量。我怎么能在REPL中这样做,以便在将来的命令中我可以将更新的图表称为g,将删除的节点称为bads?首先想到的是:

(def [g bads] (remove-bad-nodes g)

但这不起作用,因为def需要它的第一个参数是一个符号。

请注意,我不是问为什么def没有像let那样的语法;关于那个问题已经a question了。我想知道在REPL中使用返回“多个值”的函数的方便,实用的方法是什么。如果有一些原因,为什么在正常的Clojure实践中不需要在REPL中进行结构化,因为你做了别的事情,解释这可能会有用。我最近经常遇到这个问题,这就是我要问的原因。通常,但并非总是如此,这些函数会返回某些内容的更新版本以及其他一些信息。在副作用代码中,该函数将修改对象并仅返回一个值(示例中已删除的节点),但显然这不是Clojurely的方法。

3 个答案:

答案 0 :(得分:1)

我认为在repl中使用这些函数的方法只是不def你的中间结果,除非它们特别有趣;对于有趣的足够的中间结果来说,将def用于单个名称或在解构形式中写入多个def并不是一件容易的事。

例如,而不是

(def [x y] (foo))
(def [a b] (bar x y))
你可以写

(let [[x y] (foo),
      [x' y'] (bar x y)])
  (def a x') ; or maybe leave this out if `a` isn't that interesting
  (def b y'))

一个很好的副作用是你在repl中玩的时候编写的代码看起来与你有一天会添加到你的源文件的代码更相似,你肯定不会是def一遍又一遍,而是将它们解构,将它们传递给函数,等等。将您在repl上学到的信息调整为真实程序会更容易。

答案 1 :(得分:0)

对REPL进行解密没有什么独特之处。您的问题的答案与this question基本相同。我认为你的选择是:

(let [[light burnt just-right] (classify-toasts (make-lots-of-toast))] (prn light burnt just-right))

def

(def result (classify-toasts (make-lots-of-toast))) (def light (nth result 0)) (def burnt (nth result 1)) (def just-right (nth result 2)) 各个值:

def

或者写一个宏来为你做classify-toasts工作。

如果你的函数总是返回一个3元组/向量,你也可以考虑一个不同的表示。您也可以从{:light 1, :burnt 2, :just-right 3} 返回地图:

(:light the-map) => 1

然后当您需要其中一个值时,使用您需要的关键字对地图进行解构:

compile 'com.google.android.gms:play-services:10.0.1'
    compile 'com.google.android.gms:play-services-gcm:10.0.1'
    compile 'com.google.android.gms:play-services-location:10.0.1'

答案 2 :(得分:0)

观察:

user=> (def results [1 2 3])
#'user/results
user=> (let [[light burnt just-right] results] (def light light) (def burnt burnt) (def just-right just-right))
#'user/just-right
user=> light
1
user=> burnt
2
user=> just-right
3