计划/ Lisp的嵌套循环和递归(Scheme/Lisp nested loops and recu

2019-09-03 14:35发布

我试图解决方案的问题,其要求很高我使用嵌套循环或嵌套递归。

例如,我有两个列表,我要检查它们的笛卡尔乘积的条件。

什么是处理这些类型的问题的最佳方式? 如何简化这些类型的函数的指针?


我会详细一点,因为我的意图可能不够明确。

有规律的递归函数可能是这样的:

(define (factorial n)
  (factorial-impl n 1))

(define (factorial-impl n t)
  (if (eq? n 0)
      t
      (factorial-impl (- n 1) (* t n))))

试图写一个类似的功能,但与嵌套递归引入了复杂的的代码一个新的水平,我想知道是什么的基本模式是这些类型的功能,因为它可以得到非常难看,非常快。

作为一个具体的例子,我在寻找参观两个列表的笛卡尔积的所有项目中最简单的方法。

Answer 1:

在方案中,“地图”功能往往是非常方便的计算基于另一个列表。

事实上,在方案中,地图需要一个“正论证”功能和“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介绍)... ...并会前来免费从哈斯克尔版本由于它是懒惰的评价。

一般来说,在方案中,如果你想在某些时候,你也可以使用一个延续的循环的“爆发”(如抛出异常,但它是公认的做法在方案控制流)。

我很开心写这个!



Answer 2:

我不知道我看到的问题是什么。 我相信你有函数式编程,了解主要的是:通过组合几个简单的功能,构建复杂的功能。

例如,在这种情况下:

;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在第二个列表。



Answer 3:

有一些很好的答案已经在这里,但对于简单的嵌套函数(如您的尾递归阶乘),我更喜欢一个名为令:

(define factorial  
  (lambda (n)
    (let factorial-impl ([n n] [t 1])
      (if (eq? n 0)
        t
        (factorial-impl (- n 1) (* t n))))))


文章来源: Scheme/Lisp nested loops and recursion