我试图解决方案的问题,其要求很高我使用嵌套循环或嵌套递归。
例如,我有两个列表,我要检查它们的笛卡尔乘积的条件。
什么是处理这些类型的问题的最佳方式? 如何简化这些类型的函数的指针?
我会详细一点,因为我的意图可能不够明确。
有规律的递归函数可能是这样的:
(define (factorial n)
(factorial-impl n 1))
(define (factorial-impl n t)
(if (eq? n 0)
t
(factorial-impl (- n 1) (* t n))))
试图写一个类似的功能,但与嵌套递归引入了复杂的的代码一个新的水平,我想知道是什么的基本模式是这些类型的功能,因为它可以得到非常难看,非常快。
作为一个具体的例子,我在寻找参观两个列表的笛卡尔积的所有项目中最简单的方法。
在方案中,“地图”功能往往是非常方便的计算基于另一个列表。
事实上,在方案中,地图需要一个“正论证”功能和“n”名单,并呼吁每个列表中的每个元素对应的功能:
> (map * '(3 4 5) '(1 2 3))
(3 8 15)
但是,一个很自然的除了这将是一个“直角地图”功能,它会叫你的“正论证”功能与所有的从每个列表选择一个元素的不同方式。 我花了一段时间才能弄清楚到底如何做到这一点,但在这里你去:
; curry takes:
; * a p-argument function AND
; * n actual arguments,
; and returns a function requiring only (p-n) arguments
; where the first "n" arguments are already bound. A simple
; example
; (define add1 (curry + 1))
; (add1 3)
; => 4
; Many other languages implicitly "curry" whenever you call
; a function with not enough arguments.
(define curry
(lambda (f . c) (lambda x (apply f (append c x)))))
; take a list of tuples and an element, return another list
; with that element stitched on to each of the tuples:
; e.g.
; > (stitch '(1 2 3) 4)
; ((4 . 1) (4 . 2) (4 . 3))
(define stitch
(lambda (tuples element)
(map (curry cons element) tuples)))
; Flatten takes a list of lists and produces a single list
; e.g.
; > (flatten '((1 2) (3 4)))
; (1 2 3 4)
(define flatten
(curry apply append))
; cartesian takes two lists and returns their cartesian product
; e.g.
; > (cartesian '(1 2 3) '(4 5))
; ((1 . 4) (1 . 5) (2 . 4) (2 . 5) (3 . 4) (3 . 5))
(define cartesian
(lambda (l1 l2)
(flatten (map (curry stitch l2) l1))))
; cartesian-lists takes a list of lists
; and returns a single list containing the cartesian product of all of the lists.
; We start with a list containing a single 'nil', so that we create a
; "list of lists" rather than a list of "tuples".
; The other interesting function we use here is "fold-right" (sometimes called
; "foldr" or "reduce" in other implementations). It can be used
; to collapse a list from right to left using some binary operation and an
; initial value.
; e.g.
; (fold-right cons '() '(1 2 3))
; is equivalent to
; ((cons 1 (cons 2 (cons 3 '())))
; In our case, we have a list of lists, and our binary operation is to get the
; "cartesian product" between each list.
(define cartesian-lists
(lambda (lists)
(fold-right cartesian '(()) lists)))
; cartesian-map takes a n-argument function and n lists
; and returns a single list containing the result of calling that
; n-argument function for each combination of elements in the list:
; > (cartesian-map list '(a b) '(c d e) '(f g))
; ((a c f) (a c g) (a d f) (a d g) (a e f) (a e g) (b c f)
; (b c g) (b d f) (b d g) (b e f) (b e g))
(define cartesian-map
(lambda (f . lists)
(map (curry apply f) (cartesian-lists lists))))
没有所有的意见,我们有一些更紧凑的功能定义语法:
(define (curry f . c) (lambda x (apply f (append c x))))
(define (stitch tuples element)
(map (curry cons element) tuples))
(define flatten (curry apply append))
(define (cartesian l1 l2)
(flatten (map (curry stitch l2) l1)))
(define cartesian-lists (curry fold-right cartesian '(()))))
(define (cartesian-map f . lists)
(map (curry apply f) (cartesian-lists lists)))
我认为以上是合理的“优雅” ......直到有人向我展示了相当的哈斯克尔定义:
cartes f (a:b:[]) = [ f x y | x <- a , y <- b ]
cartes f (a:b:bs) = cartes f ([ f x y | x <- a , y <- b ]:bs)
2线!
我不是我的执行效率非常有信心 - 尤其是“扁平化”的步骤是快写,但最终可能会调用“追加”一个非常大的数量的列表,这可能会或可能不会对某些方案实现起来非常有效。
对于最终的实用性/有效性你会想,如果你喜欢,可以采取“懒洋洋地评估”列出/流/迭代器,而不是完全指定列表....一个“笛卡尔图流”功能的版本,会再返回“流”的结果...但是这取决于上下文(我想到了‘流’的概念在SICP介绍)... ...并会前来免费从哈斯克尔版本由于它是懒惰的评价。
一般来说,在方案中,如果你想在某些时候,你也可以使用一个延续的循环的“爆发”(如抛出异常,但它是公认的做法在方案控制流)。
我很开心写这个!
我不知道我看到的问题是什么。 我相信你有函数式编程,了解主要的是:通过组合几个简单的功能,构建复杂的功能。
例如,在这种情况下:
;compute the list of the (x,y) for y in l
(define (pairs x l)
(define (aux accu x l)
(if (null? l)
accu
(let ((y (car l))
(tail (cdr l)))
(aux (cons (cons x y) accu) x tail))))
(aux '() x l))
(define (cartesian-product l m)
(define (aux accu l)
(if (null? l)
accu
(let ((x (car l))
(tail (cdr l)))
(aux (append (pairs x m) accu) tail))))
(aux '() l))
你识别不同的步骤:获得笛卡尔积,如果“循环”在第一列表中,你将必须能够计算的列表(x,y)
用于y
在第二个列表。
有一些很好的答案已经在这里,但对于简单的嵌套函数(如您的尾递归阶乘),我更喜欢一个名为令:
(define factorial
(lambda (n)
(let factorial-impl ([n n] [t 1])
(if (eq? n 0)
t
(factorial-impl (- n 1) (* t n))))))