在Clojure中处理这个序列转换的最佳方法是什么?

egg*_*tax 8 functional-programming clojure

我是Clojure的新手,我一直在翻译我最近做的一些数据处理工作,以帮助学习.我有一个功能翻译,工作正常,而且更短,但感觉更不易读.任何人都可以建议更可读和/或更惯用的方式来处理这个问题吗?

在Python中:

def createDifferenceVector(v,startWithZero=True):
   deltas = []
   for i in range(len(v)):
       if i == 0:
           if startWithZero:
               deltas.append(0.0)
           else:
               deltas.append(v[0])
       else:
           deltas.append(v[i] - v[i-1])
   return deltas
Run Code Online (Sandbox Code Playgroud)

我尝试Clojure翻译:

(defn create-diff-vector [v start-zero]
  (let [ext-v (if start-zero
                (cons (first v) v)
                (cons 0 v))]
    (for [i (range 1 (count ext-v))] 
      (- (nth ext-v i) (nth ext-v (- i 1))))))
Run Code Online (Sandbox Code Playgroud)

可能因为我对Clojure缺乏经验而不太可读,但特别是,将元素添加到输入向量的技巧让我觉得它模糊了意图.我试过的所有没有使用前置技巧的解决方案都要长得多,而且更加丑陋.

在Clojure中,许多序列转换非常优雅,但到目前为止我发现的那些是像这样的,a)适合于通过索引而不是元素进行操作,和/或b)需要对某些元素进行特殊处理.

谢谢你的任何建议.

Dan*_*nus 12

惯用语Clojure倾向于整体操纵序列,而不是单个元素.您可以create-diff-vector用英语定义为:

结果是一个由以下内容组成的向量:

  • 输入的零或第一个元素start-zero,分别取决于是真还是假; 其次是
  • 没有第一个元素的输入序列和没有最后一个元素的输入序列之间的差异.

第二部分可以这样说明:对于输入(31 41 59 26 53),我们有

  input without the first element:   (41 59  26 53)
- input without the last element:    (31 41  59 26)
===================================================
  result:                            (10 18 -33 27)

其中,翻译成Clojure,变得非常简洁:

(defn diff-vector [v start-zero?]
  (into [(if start-zero? 0 (first v))]
    (map - (rest v) v))))
Run Code Online (Sandbox Code Playgroud)

需要注意的几点:

  • 最后的问号start-zero?用作暗示在这里预期布尔值的提示.
  • 该代码利用了这样的事实:map在不同长度的序列上ping函数在最短序列结束时终止.

  • 很酷.我总是忘记我可以做到这一点`(map foo x(rest x))`thing - 我通常会找到`partition`,比如`(for [[ab](partition 2 1 v)]( - ba) )`.地图更好,感谢提醒. (2认同)