对于Clojure和编程来说仍然是一个新手,所以请原谅这个愚蠢的问题。
问题是:
找到n和k,使得最多n(不包括)的数字之和等于从n + 1到k(包括)的数字之和。
我的解决方案(工作正常)是定义以下功能:
(defn addd [x] (/ (* x (+ x 1)) 2))
(defn sum-to-n [n] (addd(- n 1)))
(defn sum-to-k [n=1 k=4] (- (addd k) (addd n)))
(defn is-right[n k]
(= (addd (- n 1)) (sum-to-k n k)))
然后运行以下循环:
(loop [n 1 k 2]
(cond
(is-right n k) [n k]
(> (sum-to-k n k) (sum-to-n n) )(recur (inc n) k)
:else (recur n (inc k))))
这只返回一个答案,但如果我手动设置n和k,我可以得到不同的值。但是,我想定义一个函数,它返回所有值的延迟序列,以便:
(= [6 8] (take 1 make-seq))
如何尽可能高效地完成此操作?我尝试了各种各样的东西,但没有多少运气。
由于
:编辑:
我想我想出了一个更好的方法,但它的回归应该是一个矢量'。 Clojure docs没什么帮助......
继承新代码:
(defn calc-n [n k]
(inc (+ (* 2 k) (* 3 n))))
(defn calc-k [n k]
(inc (+ (* 3 k)(* 4 n))))
(defn f
(let [n 4 k 6]
(recur (calc-n n k) (calc-k n k))))
(take 4 (f))
答案 0 :(得分:2)
是的,您可以创建一个lazy-seq,以便下一次迭代将获取上一次迭代的结果。这是我的建议:
(defn cal [n k]
(loop [n n k k]
(cond
(is-right n k) [n k]
(> (sum-to-k n k) (sum-to-n n) )(recur (inc n) k)
:else (recur n (inc k)))))
(defn make-seq [n k]
(if-let [[n1 k1] (cal n k)]
(cons [n1 k1]
(lazy-seq (make-seq (inc n1) (inc k1))))))
(take 5 (make-seq 1 2))
;;=> ([6 8] [35 49] [204 288] [1189 1681] [6930 9800])
答案 1 :(得分:1)
只使用iterate
生成候选对象的懒惰seq然后过滤它们应该是你需要的:
(def pairs
(->> [1 2]
(iterate (fn [[n k]]
(if (< (sum-to-n n) (sum-n-to-k n k))
[(inc n) k]
[n (inc k)])))
(filter (partial apply is-right))))
user> (take 5 pairs)
;;=> ([6 8] [35 49] [204 288] [1189 1681] [6930 9800])
在语义上它就像手动生成一个lazy-seq一样,应该同样有效,但这个可能更惯用
答案 2 :(得分:0)
这个第一个函数可能有更好的数学名称,但我不太了解数学。我使用inc
(增量)代替(+ ,,, 1)
,但这只是个人偏好。
(defn addd [x]
(/ (* x (inc x)) 2))
我会稍微清理这里的间距并使用dec
(减量)函数。
(defn sum-to-n [n]
(addd (dec n)))
(defn sum-n-to-k [n k]
(- (addd k) (addd n)))
在某些语言谓词中,返回布尔值的函数,
有is-odd
或is-whatever
之类的名称。在clojure他们通常
称为odd?
或whatever?
。
问号不是语法,它只是名称的一部分。
(defn matching-sums? [n k]
(= (addd (dec n)) (sum-n-to-k n k)))
循环特殊形式有点像匿名函数
让recur跳回去。如果没有循环形式,则重复跳回
到封闭的功能。
另外,不知道该怎么称呼它,所以我只称它为f
。
(defn f [n k]
(cond
(matching-sums? n k) [n k]
(> (sum-n-to-k n k) (sum-to-n n)) (recur (inc n) k)
:else (recur n (inc k))))
(comment
(f 1 2) ;=> [6 8]
(f 7 9) ;=> [35 49]
)
现在,针对您的实际问题。如何制作一个懒惰的序列。您可以使用lazy-seq
宏,就像在minhtuannguyen的答案中一样,但是有一种更简单,更高级的方式。使用iterate
功能。 iterate
接受一个函数和一个值,并返回值的无限序列,然后调用带有值的函数,然后调用上的函数值等。
(defn make-seq [init]
(iterate (fn [n-and-k]
(let [n (first n-and-k)
k (second n-and-k)]
(f (inc n) (inc k))))
init))
(comment
(take 4 (make-seq [1 2])) ;=> ([1 2] [6 8] [35 49] [204 288])
)
通过在匿名函数的参数向量中使用解构,可以简化一点。
(defn make-seq [init]
(iterate (fn [[n k]]
(f (inc n) (inc k)))
init))
编辑:
关于f
中的重复计算。
通过使用let
保存计算结果,您可以避免为每个数字多次计算addd
。
(defn f [n k]
(let [to-n (sum-to-n n)
n-to-k (sum-n-to-k n k)]
(cond
(= to-n n-to-k) [n k]
(> n-to-k to-n) (recur (inc n) k)
:else (recur n (inc k)))))
答案 3 :(得分:-1)
如果您不想“自己动手”,这是另一种解决方案。我还通过重命名/重新格式化来清理算法。
主要区别在于您将loop-recur视为t/lazy-gen
形式内的无限循环。当您找到要保留的值时,可以使用t/yield
表达式来创建延迟的输出序列。此结构是 生成器函数的Clojure版本 。
(ns tst.demo.core
(:use tupelo.test )
(:require [tupelo.core :as t] ))
(defn integrate-to [x]
(/ (* x (+ x 1)) 2))
(defn sum-to-n [n]
(integrate-to (- n 1)))
(defn sum-n-to-k [n k]
(- (integrate-to k) (integrate-to n)))
(defn sums-match[n k]
(= (sum-to-n n) (sum-n-to-k n k)))
(defn recur-gen []
(t/lazy-gen
(loop [n 1 k 2]
(when (sums-match n k)
(t/yield [n k]))
(if (< (sum-to-n n) (sum-n-to-k n k))
(recur (inc n) k)
(recur n (inc k))))))
(t/spyx (take 5 (recur-gen)))
您可以找到所有详细信息in the Tupelo Library。