【问题标题】:What's the best way to handle this sequence transformation in Clojure?在 Clojure 中处理这种序列转换的最佳方法是什么?
【发布时间】:2011-11-17 12:27:30
【问题描述】:

我是 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

我对 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))))))

这可能是因为我对 Clojure 缺乏经验,所以它的可读性较差,但特别是,在输入向量前添加元素的技巧让我觉得它掩盖了意图。我尝试过的所有未使用前置技巧的解决方案都更长且更丑陋。

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

感谢您的任何建议。

【问题讨论】:

    标签: clojure functional-programming


    【解决方案1】:

    惯用的 Clojure 倾向于将序列作为一个整体来操作,而不是单个元素。您可以将英文中的create-diff-vector 定义为:

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

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

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

    没有第一个元素的输入:(41 59 26 53) - 没有最后一个元素的输入:(31 41 59 26) ==================================================== = 结果:(10 18 -33 27)

    翻译成 Clojure 后变得非常简洁:

    (defn diff-vector [v start-zero?]
      (into [(if start-zero? 0 (first v))]
        (map - (rest v) v))))
    

    注意几点:

    • start-zero? 末尾的问号表示此处应为布尔值。
    • 该代码利用了这样一个事实:mapping 函数在不同长度的序列上终止于最短序列的末尾。

    【讨论】:

    • 非常酷。我总是忘记我可以做到(map foo x (rest x)) 的事情——我通常会使用partition 来代替(for [[a b] (partition 2 1 v)] (- b a))。地图好多了,谢谢提醒。
    • 谢谢。这是一个非常清晰的解释,也是一种更优雅的表达方式。我肯定要花一些时间来理解 Clojure 的思维方式,但我已经看到了回报。
    • 嘿。五年后,我现在是一名专业的 Clojure/脚本开发人员,我只是碰巧再次遇到了这个问题。独立编写了一个解决方案,它最终几乎与你的一样,@daniel。感谢您当时帮助我走上正确的道路:)
    【解决方案2】:

    这个实现会更惯用:

    (defn create-diff-vector [v start-with-zero?]
      (let [v (cons (if start-with-zero? (first v) 0) v)]
           (map - (rest v) v)))
    

    我首先将向量的第一个值或 0 添加到输入向量。然后我使用map 将向量从自身中减去,移动一个位置。

    【讨论】:

      猜你喜欢
      • 2021-03-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-15
      • 1970-01-01
      • 2019-06-02
      • 1970-01-01
      • 1970-01-01
      • 2010-09-18
      相关资源
      最近更新 更多