scheme - 方案中的配对组合
问题描述
我正在尝试找到可以使用方案中的 N 对列表进行的各种组合。这是我到目前为止的位置:
(define (pair-combinations list-of-pairs)
(if (null? list-of-pairs)
nil
(let ((first (caar list-of-pairs))
(second (cadar list-of-pairs))
(rest (pair-combinations (cdr list-of-pairs))))
(append
(list (cons first rest))
(list (cons second rest))
))))
现在,我不确定逻辑是否正确,但我立即注意到的是括号的伸缩。例如:
(define p1 '( (1 2) (3 4) (5 6) ))
(pair-combinations p1)
((1 (3 (5) (6)) (4 (5) (6))) (2 (3 (5) (6)) (4 (5) (6))))
显然这是来自list (...
append 调用中的重复,所以结果看起来像(list 1 (list 2 (list 3 ...
. 有没有办法在单个函数中执行上述操作?如果是这样,我哪里出错了,如何正确完成?
我希望得到的答案是:
((1 3 5) (1 3 6) (1 4 5) (1 4 6) (2 3 5) (2 3 6) (2 4 5) (2 4 6))
也就是说,从 N 对中选择一个元素的可能方式。
解决方案
这是思考这个问题的一种方法。如果输入为空列表,则结果为()
。如果输入是包含单个列表的列表,则结果只是映射list
到该列表的结果,即(combinations '((1 2 3)))
--> ((1) (2) (3))
。
否则,可以通过获取输入中的第一个列表并将该列表中的每个项目添加到输入中其余列表的所有组合来形成结果。也就是说,(combinations '((1 2) (3 4)))
可以通过将 的每个元素添加(1 2)
到 中的每个组合中来找到(combinations '((3 4)))
,它们是((3) (4))
。
用两个程序来表达这一点似乎很自然。首先,一个combinations
程序:
(define (combinations xss)
(cond ((null? xss) '())
((null? (cdr xss))
(map list (car xss)))
(else
(prepend-each (car xss)
(combinations (cdr xss))))))
现在prepend-each
需要一个程序:
(define (prepend-each xs yss)
(apply append
(map (lambda (x)
(map (lambda (ys)
(cons x ys))
yss))
xs)))
在这里,该过程prepend-each
接受一个列表xs
和一个列表列表,yss
并返回将每个x
in添加xs
到列表 in的结果yss
。内部映射将每个列表ys
放入yss
其中并在其上包含一个x
from xs
。由于内部映射产生一个列表列表,而外部映射然后产生一个列表列表的列表,append
用于在返回之前加入结果。
combinations.rkt> (combinations '((1 2) (3 4) (5 6)))
'((1 3 5) (1 3 6) (1 4 5) (1 4 6) (2 3 5) (2 3 6) (2 4 5) (2 4 6))
现在已经找到了一种可行的方法,可以将其转换为单个过程:
(define (combinations-2 xss)
(cond ((null? xss) '())
((null? (cdr xss))
(map list (car xss)))
(else
(apply append
(map (lambda (x)
(map (lambda (ys)
(cons x ys))
(combinations-2 (cdr xss))))
(car xss))))))
但是,我不会那样做,因为两个程序中的第一个版本似乎更清楚。
prepend-each
仅查看使用和不使用的结果可能会有所帮助append
:
combinations.rkt> (prepend-each '(1 2) '((3 4) (5 6)))
'((1 3 4) (1 5 6) (2 3 4) (2 5 6))
不使用append
:
(define (prepend-each-no-append xs yss)
(map (lambda (x)
(map (lambda (ys)
(cons x ys))
yss))
xs))
combinations.rkt> (prepend-each-no-append '(1 2) '((3 4) (5 6)))
'(((1 3 4) (1 5 6)) ((2 3 4) (2 5 6)))
可以看出,1
添加到每个列表中((3 4) (5 6))
以创建列表列表,然后2
添加到每个列表中((3 4) (5 6))
以创建列表列表。这些结果包含在另一个列表中,因为1
和2
来自外部映射(1 2)
。这就是为什么append
用来加入结果的原因。
一些最终改进
请注意,prepend-each
当为空时返回一个空列表yss
,但是xs
当包含一个空列表时,返回一个包含分布在尽可能多列表中的元素的yss
列表:
combinations.rkt> (prepend-each '(1 2 3) '(()))
'((1) (2) (3))
combinations
当输入包含单个列表时,这与我们想要的结果相同。我们可以修改combinations
为有一个基本情况:当输入为 时'()
,结果为(())
。这将允许prepend-each
完成之前由 完成的工作(map list (car xss))
,从而combinations
更加简洁;该prepend-each
程序没有改变,但为了完整起见,我将其包括在下面:
(define (combinations xss)
(if (null? xss) '(())
(prepend-each (car xss)
(combinations (cdr xss)))))
(define (prepend-each xs yss)
(apply append
(map (lambda (x)
(map (lambda (ys)
(cons x ys))
yss))
xs)))
更简洁combinations
之后,我可能会想继续把它写成一个过程,毕竟:
(define (combinations xss)
(if (null? xss) '(())
(apply append
(map (lambda (x)
(map (lambda (ys)
(cons x ys))
(combinations (cdr xss))))
(car xss)))))
推荐阅读
- typescript - 枚举映射附加约束的条件类型
- android - 在一个 gradle 文件中设置所有依赖项?
- java - 如何将字符串分成小部分写入文件以节省内存?
- javascript - 使用 js 函数的整数不保留第一个 0
- ansible - 遍历字典,其中键的值再次是 ansible 中的列表
- tableview - TreeTableView 中不可编辑的子节点
- haskell - 如何在haskell中正确键入可选参数
- oracle - 取消透视 Oracle 中的多个列不起作用
- html - 如何使用 flex 进行响应式设计?
- postgresql - Docker-compose postgreSQL 权限错误