【问题标题】:Mysterious Clojure function神秘的 Clojure 函数
【发布时间】:2020-05-08 11:41:55
【问题描述】:

我想编写一个具有以下行为的 clojure 函数:

  (take 4 (floyd))
  => '((1) (2 3) (4 5 6) (7 8 9 10))

  (take 3 (floyd))
  => '((1) (2 3) (4 5 6))

  (take 1 (floyd))
  => '((1)))

我尝试使用partitionpartition-all 来验证这些测试,但是我找不到正确的解决方案。如果您对如何做有任何想法,我将非常感谢您的帮助。几周前我开始使用 clojure,但仍有一些问题。
谢谢

【问题讨论】:

  • 您尝试过递归解决方案吗?请在此处发布您的尝试
  • 请添加您尝试过的代码以及失败的原因(例如错误、堆栈跟踪、日志等),以便我们对其进行改进。
  • 我试过``` (defn f ([] (f (range))) ([s] (lazy-seq (cons (partition (take (first s) s) s) (f (drop (first s) s)))))```

标签: clojure declarative


【解决方案1】:

这是另一个选择:

(defn floyd []
  (map (fn [lo n] (range lo (+ lo n 1))) 
       (reductions + 1 (iterate inc 1)) 
       (range)))

(take 5 (floyd))
;=> ((1) (2 3) (4 5 6) (7 8 9 10) (11 12 13 14 15))

这是基于您想要一系列递增范围的观察得出的(map(range) 参数用于生成越来越长的范围序列),每个序列几乎都从三角形数字序列开始:

(take 5 (reductions + 0 (iterate inc 1)))
;=> (0 1 3 6 10)

如果我们改为从1 开始该序列,我们将获得您想要的序列中的起始数字:

(take 5 (reductions + 1 (iterate inc 1)))
;=> (1 2 4 7 11)

如果映射函数中的 + 1 困扰您,您可以这样做:

(defn floyd []
  (map (fn [lo n] (range lo (+ lo n)))
       (reductions + 1 (iterate inc 1))
       (iterate inc 1)))

【讨论】:

  • 谢谢,我不知道reductions 功能。你的例子很有启发性。
  • 如果有机会,你能帮我写一个我尝试用 Clojure 编写的宏吗?
  • 宏俱乐部的第一条规则:不要写宏 :) 我在 StackOverflow 上没有回答很多问题。我将大部分精力集中在 Clojurians Slack 上,因为我更喜欢“聊天”系统的交互性。
【解决方案2】:

不可能用partition / partition-all 解决它,因为它们将您的序列分成预定义大小的块。

您可以做的是为此使用递归惰性函数:

user> (defn floyd []
        (letfn [(f [n rng]
                  (cons (take n rng)
                        (lazy-seq (f (inc n) (drop n rng)))))]
          (f 1 (iterate inc 1))))
#'user/floyd

user> (take 1 (floyd))
;;=> ((1))

user> (take 2 (floyd))
;;=> ((1) (2 3))

user> (take 3 (floyd))
;;=> ((1) (2 3) (4 5 6))

user> (take 4 (floyd))
;;=> ((1) (2 3) (4 5 6) (7 8 9 10))

另一个变体可以使用类似的方法,但只跟踪 chunk-start/chunk-size:

user> (defn floyd []
        (letfn [(f [n start]
                  (cons (range start (+ start n))
                        (lazy-seq (f (inc n) (+ start n)))))]
          (f 1 1)))

另一种做法是使用clojure的集合操作函数:

user> (defn floyd-2 []        
        (->> [1 1]
             (iterate (fn [[start n]]
                        [(+ n start) (inc n)]))
             (map (fn [[start n]] (range start (+ start n))))))
#'user/floyd-2

user> (take 4 (floyd-2))
;;=> ((1) (2 3) (4 5 6) (7 8 9 10))

user> (take 5 (floyd-2))
;;=> ((1) (2 3) (4 5 6) (7 8 9 10) (11 12 13 14 15))

user> (take 1 (floyd-2))
;;=> ((1))

【讨论】:

  • 谢谢,我是 Clojure 的新手。我最近开始学习它,懒惰对我来说是一个相当新的概念。我习惯于在 lazy-seq 中写入 cons 。谢谢你帮助我。我真的很感激这一点,它对我的​​家庭作业有很大帮助
  • 使用集合操作函数的方案相当优雅。在不使用 lazy-seq 的情况下保留了惰性。我使用 clojure 内置函数的次数越多,我就会获得奖励积分(仅在有好处时)
【解决方案3】:

这个怎么样:

(defn floyd []
  (map (fn[n]
         (let [start (/ (* n (inc n)) 2)]
           (range (inc start) (+ start n 2))))
       (iterate inc 0)))

(take 4 (floyd))

【讨论】:

  • 非常感谢。我喜欢它的写作方式。没想到会这么简洁明了。
  • (iterate inc 0)(range) 并且很可能效率较低。将其转换为 for 使其更加紧凑:(defn floyd [] (for [n (range) :let [start (inc (/ (* n (inc n)) 2))]] (range start (inc (+ start n)))))
猜你喜欢
  • 2018-05-15
  • 2017-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多