Clojure 编写一个尽可能懒惰的类展开器函数来生成任意因子分解 问题表述

Clojure 编写一个尽可能懒惰的类展开器函数来生成任意因子分解 问题表述,clojure,lisp,factorization,lazy-sequences,unfold,Clojure,Lisp,Factorization,Lazy Sequences,Unfold,非正式地说,我想写一个函数,它以一个生成二进制分解的函数和一个元素(通常是中性的)作为输入,创建一个任意长度的分解生成器。更具体地说,让我们首先在Clojure中定义函数nfoldr (defn nfoldr[f e] (fn rec[n] (fn[s] (如果(零?n) (如果(空?s)e) (如有)(续) (如果部分[x((rec(dec n))(其余的s))] (f(名单(首s)x(()))(""""" 这里使用的nil的意思是“未定义的输出,输入不在函数的域中”。此外,让我们将函数f的

非正式地说,我想写一个函数,它以一个生成二进制分解的函数和一个元素(通常是中性的)作为输入,创建一个任意长度的分解生成器。更具体地说,让我们首先在Clojure中定义函数
nfoldr

(defn nfoldr[f e]
(fn rec[n]
(fn[s]
(如果(零?n)
(如果(空?s)e)
(如有)(续)
(如果部分[x((rec(dec n))(其余的s))]
(f(名单(首s)x(()))("""""
这里使用的
nil
的意思是“未定义的输出,输入不在函数的域中”。此外,让我们将函数
f
的逆关系视为定义
inv(f)(y)={x | f(x)=y}
的集值函数

我想定义一个函数
nunfoldr
,使得
inv(nfoldr(f,e)(n))=nunfoldr(inv(f),e)(n)
当每个元素
y
inv(f)(y)
是有限的,对于每个二进制函数
f
,元素
e
和自然数
n

此外,我希望因子分解尽可能地以二维惰性的方式生成。我的目标是,当第一次得到某个部分的因式分解时,下一部分或下一个因式分解不需要(太多)计算。类似地,当第一次得到一个因式分解时,不会发生下一个因式分解所需的计算,而之前的所有因式分解实际上都完全实现了

在替代公式中,我们可以使用以下较长版本的
nfoldr
,当
e
为中性元素时,这相当于较短版本

(defn nfoldr[f e]
(fn[n]
(fn[s]
(如果(零?n)
(如果(空?s)e)
((fn rec[n]
(fn[s]
(如果(=1 n)
(如果(和(序号)(空)(其余)(第一个))
(如有)(续)
(如果部分[x((rec(dec n))(其余的s))]
(f(列表(前s)(""""""))
n) )))

特例 此问题是中描述的生成分区问题的推广。让我们看看如何将旧问题简化为当前问题。对于每个自然数
n
,我们都有:

npt(n)=inv(ncocat(n))=inv(ncoldr(concat2,())(n))=nunfoldr(inv(concat2),())(n)=nunfoldr(pt2,())(n)

其中:

  • npt(n)
    生成
    n
    -ary分区
  • nconcat(n)
    计算
    n
    -ary串联
  • concat2
    计算二进制连接
  • pt2
    生成二进制分区
下面的定义给出了这个问题的解决方案

(defn生成[步骤开始]
(fn[x](花一段时间?(迭代步骤(开始x ')))
(定义pt2步骤[[x y]]
(如果(序列y)(列表x(列表第一个y))(剩余y)))
(def pt2启动(部分列表())
(def pt2(生成pt2步骤pt2启动))
(def npt(nunfoldr pt2())

我将总结解决此问题的经验,使用旧的方法创建示例运行,并总结一些观察结果和扩展建议


解决方案0 起初,我改进/概括了我解决老问题的方法。在这里,我编写了自己的
concat
map
版本,主要是为了更好地展示,在
concat
的情况下,为了增加一些惰性。当然,我们可以使用Clojure的版本或
mapcat

(defn fproduct[f]
(fn[s]
(续)
(如有)(及(以下f)(以下s))
(缺点
((首f)(首s))
((fproduct(rest f))(rest s()()()()))
(defn concat'[s]
(续)
(如有)(续)
(如果让[x(序号(第一个))]
(前十名被告)(后十名被告)(后十名被告)(后十名被告))
("""""""""其他""""""
(定义地图'[f]
(新闻公报)
(续)
(如有)(续)
(反对党(f(前s))(反对党(其余s()())()))
(defn nunfoldr[f e]
(fn rec[n]
(fn[x]
(如果(零?n)
(如果(=ex)(列表())
((比较)
康卡特
(地图)(公司)
(局部应用映射)
(F)产品(列表
(部分cons)
(rec(dec n‘‘‘‘‘‘)’)
(f)
x) )))
为了获得内心的懒惰,我们可以用类似于
(comp(partial concat)list)
的东西来代替
(偏cons)
。虽然这样我们得到了内部的
LazySeq
s,但我们没有获得任何有效的惰性,因为在
cons
ing之前,完全实现
rest
部分所需的大部分计算都发生了,这在这种通用方法中似乎是不可避免的。基于较长版本的
nfoldr
,我们还可以定义以下更快的版本

(defn nunfoldr[f e]
(fn[n]
(fn[x]
(如果(零?n)
(如果(=ex)(列表())
((fn rec[n]
(fn[x](println\)
(如果(=1 n)
(名单(名单x))
((比较)
康卡特
(地图)(公司)
(局部应用映射)
(F)产品(列表
(部分cons)
(rec(dec n‘‘‘‘‘‘)’)
(f)
x) ))
n)
x) )))
在这里,我在主递归函数中添加了一个
println
调用,以获得一些渴望的可视化效果。因此,让我们展示外在的懒惰和内在的渴望

user=>(第一个((npt 5)(范围3)))
< (0 1 2) >
< (0 1 2) >
< (0 1 2) >
< (0 1 2) >
< (0 1 2) >
(() () () () (0 1 2))
用户=>(ffirst