所以,如果我有以下内容,它返回一组四个数字中的最小值:
(define (minimum2 a b c d)
(cond ((and (< a b) (< a c) (< a d)) a)
((and (< b c) (< b d)) b)
((< c d) c)
(else d)))
但是,我想写它以便我比较a到b并找到这两者之间的最小值,然后比较c和d,找到它们之间的最小值,然后将这两个最小值进行比较以找到实际的最小值。如果我写的内容很难理解,可以把它想象成一个锦标赛支架,其中“播放”b,而获胜者则扮演c和d之间的另一个赢家。提前感谢您的帮助!
答案 0 :(得分:2)
这是一种方法:
(define (min4 a b c d)
(define (min2 x y)
(if (< x y) x y))
(min2 (min2 a b) (min2 c d)))
另一种方法,如果你不想使用内部函数:
(define (min4 a b c d)
(let ((min-ab (if (< a b) a b))
(min-cd (if (< c d) c d)))
(if (< min-ab min-cd) min-ab min-cd)))
答案 1 :(得分:2)
以下是两种方法。我认为第一个使用reduce
更加惯用,但它没有采用锦标赛风格结构,尽管它使用了相同数量的比较。第二个是锦标赛风格结构,实际上只是一个广义合并排序的特例。比较次数相同的原因是锦标赛风格比较,
min(a,b,c,d)= min(min(a,b),min(c,d))
并在reduce
表述中,
min(a,b,c,d)= min(min(min(a,b),c),d)
两者都需要三次调用最低级别的min程序。
reduce
的方法根据我的经验,此解决方案使用fold
(在Lisp语言中通常称为reduce
)。 Scheme(R 5 RS)不包括reduce
或折叠,但它很容易实现:
(define (reduce function initial-value list)
(if (null? list)
initial-value
(reduce function (function initial-value (car list))
(cdr list))))
左关联折叠是尾递归且高效的。给定二进制函数 f ,初始值 i ,列表[ x 1 ,..., x n ],它返回f(f(... f(f( i , x 1 ), x 2 )...), x n -1 ), x n )。
在这种情况下,二进制函数是min2
。 R5R5实际上已经包含了一个 n -ary(好吧,它实际上至少需要一个参数,它是 at-least-one -ary)min
,这意味着min
已经可以作为二元函数使用了,但是如果你想使用内置的min
,那么你首先要做(min a b c d)
。所以,为了完整起见,这里有一个min2
,它只接受两个参数。
(define (min2 a b)
(if (< a b)
a
b))
然后我们的 n -ary min*
只是在初始值和列表上减少min2
。我们可以在参数列表中使用.
表示法使其成为需要至少一个参数的可变参数函数。这意味着除了更典型的多参数调用之外,我们还可以(min* x) => x
。
(define (min* a . rest)
(reduce min2 a rest))
例如:
(min* 4 2 1 3)
;=> 1
正确的锦标赛风格min
实际上与merge sort同构。合并排序递归地将列表拆分为一半(这可以使用原始列表的索引就地完成,而不是实际将列表拆分成新列表),直到产生长度为1的列表。然后相邻列表合并以生成长度为2的列表。然后,合并长度为2的相邻列表以生成长度为4的列表,依此类推,直到只有一个排序列表。 (如果输入列表的长度不是2的幂,则这里的数字并不总是完美的,但是同样的原则适用。)如果你编写一个合并排序的实现,它将合并函数作为参数,然后你可以让它返回包含较小值的一个元素列表。
首先,我们需要一个将列表拆分为左侧和右侧的函数:
(define (split lst)
(let loop ((left '())
(right lst)
(len (/ (length lst) 2)))
(if (< len 1)
(list (reverse left) right)
(loop (cons (car right) left)
(cdr right)
(- len 1)))))
> (split '(1 2 3 4))
((1 2) (3 4))
> (split '(1))
(() (1))
> (split '(1 2 3))
((1) (2 3))
合并排序现在很容易实现:
(define (merge-sort list merge)
(if (or (null? list) (null? (cdr list)))
list
(let* ((sides (split list))
(left (car sides))
(right (cadr sides)))
(merge (merge-sort left merge)
(merge-sort right merge)))))
我们仍然需要合并程序。我们需要一个可以采用两个列表的列表,而不是采用两个列表并返回其已排序元素列表的标准列表,其中每个列表最多包含一个元素,并且最多一个列表可能为空。如果任一列表为空,则返回非空列表。如果两个列表都是非空的,则返回具有较小元素的列表。我称之为min-list
。
(define (min-list l1 l2)
(cond
((null? l1) l2)
((null? l2) l1)
(else (if (< (car l1) (car l2))
l1
l2))))
在这种情况下,您可以定义min*
来调用merge-sort
,其中合并过程为min-list
。 Merge-sort将返回包含一个元素的列表,因此我们需要car
从列表中获取该元素。
(define (min* a . rest)
(car (merge-sort (cons a rest) min-list)))
(min* 7 2 3 6)
;=> 2