我试图使用通常的对角化思想枚举OCaml中来自两个惰性列表(第一个列表中的第一个元素,第二个列表中的第二个元素)中的元素组成的所有对的集合。根据严格的评估术语,这个想法就像是
enum [0;1;2;...] [0;1;2;...] = [(0,0);(0,1);(1;0);(0;2);(1;1);(2;2);...]
我的问题是:你怎么懒得定义这个?
我会解释到目前为止我的想法,也许这对任何试图回答这个问题的人都有帮助。但如果您已经知道答案,则无需进一步阅读。我可能走错了路。
我将懒惰列表定义为
type 'a node_t =
| Nil
| Cons of 'a *'a t
and 'a t = ('a node_t) Lazy.t
然后我定义了函数'seq'
let seq m =
let rec seq_ n m max acc =
if n=max+1
then acc
else (seq_ (n+1) (m-1) max (lazy (Cons((n,m),acc))))
in seq_ 0 m m (lazy Nil)
它给出了一对(x,y)对的惰性列表,使得x + y = m。这就是对角线的想法。我们首先列举所有总和为0的对,然后是所有总和为1的对,然后是总和为2的对等。
然后我定义了'enum_pair'函数
let enum_pair () =
let rec enum_pair_ n = lazy (Cons(seq n,enum_pair_ (n+1)))
in enum_pair_ 0
生成无限懒惰列表:由0对的惰性列表组合,与对1的懒惰列表连接,等等。
到现在为止,在我看来,我几乎就在那里。现在的问题是:我如何逐个获得实际的对?
在我看来,我必须使用某种形式的列表连接(懒惰的等价物@)。但这并不高效,因为在我的懒惰列表表示中,连接两个列表的复杂度为O(n ^ 2),其中n是第一个列表的大小。我应该寻找懒惰列表的不同表示吗?或者是否有其他方式(不使用上面的'seq'和'enum_pair')不需要列表连接?
任何帮助都会非常感激。
非常感谢, Surikator。
答案 0 :(得分:1)
与此同时,我已经设法到达某个地方但是,虽然它解决了问题,但解决方案并不是很优雅。在定义了我的初始问题中定义的函数之后,我可以将附加函数'enum_pair_cat'定义为
let rec enum_pair_cat ls =
lazy(
match Lazy.force ls with
| Nil -> Nil
| Cons(h,t) -> match Lazy.force h with
| Nil -> Lazy.force (enum_pair_cat t)
| Cons (h2,t2) -> Cons (h2,enum_pair_cat (lazy (Cons (t2,t))))
)
此新功能可实现所需的行为。通过做
enum_pair_cat (enum_pair ())
我们得到一个惰性列表,其中列出了所描述的对。所以,这解决了这个问题。
但是,我对此并不完全满意,因为此解决方案无法扩展到更高的枚举(例如,三个惰性列表)。如果您对如何解决枚举从n个懒惰列表中取得的所有n元组的一般问题有任何想法,请告诉我!
谢谢, Surikator。
答案 1 :(得分:1)
在Haskell中你可以写:
concatMap (\l -> zip l (reverse l)) $ inits [0..]
首先,我们生成[0..]
的所有初始细分:
> take 5 $ inits [0..]
[[],[0],[0,1],[0,1,2],[0,1,2,3]]
将其中一个段用反向拉开它给我们一个对角线:
> (\l -> zip l (reverse l)) [0..4]
[(0,4),(1,3),(2,2),(3,1),(4,0)]
因此,映射zip将提供所有对角线:
> take 10 $ concatMap (\l -> zip l (reverse l)) $ zipWith take [1..] (repeat [0..])
[(0,0),(0,1),(1,0),(0,2),(1,1),(2,0),(0,3),(1,2),(2,1),(3,0)]