我想写一个Racket函数,它接受一个列表并返回该列表中最小元素的位置。我已经编写了一个有效的函数:
(define (min-position xs)
(define (min-position2 count pos xs)
(cond ((null? xs) #f)
((= 1 (length xs)) pos)
((< (car xs) (cadr xs))
(min-position2 (+ count 1) pos (cons (car xs) (cddr xs))))
(else (min-position2 0 (+ count pos 1) (cons (cadr xs) (cddr xs))))))
(min-position2 0 0 xs))
示例输入和输出:
> (min-position '(9 8 7 6 5))
4
> (min-position '(9 8 1 6 5))
2
> (min-position '(0 1 2))
0
但有更优雅的方式来写这个吗?
答案 0 :(得分:3)
我不确定你所说的“优雅”是什么意思。例如,可能存在spiffier算法。但这是我如何在保留基本方法的同时使代码更具可读性(恕我直言)。
一步一步:
您的输入/输出示例,重写为check-equal?
测试:
#lang racket
(require rackunit)
(define (test f)
(check-equal? (f '(9 8 7 6 5)) 4)
(check-equal? (f '(9 8 1 6 5)) 2)
(check-equal? (f '(0 1)) 0)
(check-equal? (f '(0 1 2)) 0))
您的原始文件,但使用[]代替()代表cond
条款。
(define (min-position/v0 xs)
(define (min-position2 count pos xs)
(cond [(null? xs) #f]
[(= 1 (length xs)) pos]
[(< (car xs) (cadr xs))
(min-position2 (+ count 1) pos (cons (car xs) (cddr xs)))]
[else
(min-position2 0 (+ count pos 1) (cons (cadr xs) (cddr xs)))]))
(min-position2 0 0 xs))
(test min-position/v0)
使用match
对列表进行细化,并使用this
和next
等名称代替(car xs)
和(cadr xs)
:
(define (min-position/match xs)
(define (min-position2 count pos xs)
(match xs
[(list) #f]
[(list _) pos]
[(list this next more ...)
(cond [(< this next)
(min-position2 (+ count 1) pos (cons this more))]
[else
(min-position2 0 (+ count pos 1) (cons next more))])]))
(min-position2 0 0 xs))
(test min-position/match)
将内部功能更改为let loop ...
。真的是一样的,只是更简洁一点。
(define (min-position/match&loop xs)
(let loop ([count 0] [pos 0] [xs xs])
(match xs
[(list) #f]
[(list _) pos]
[(list this next more ...)
(cond [(< this next) (loop (+ count 1) pos (cons this more))]
[else (loop 0 (+ count pos 1) (cons next more))])])))
(test min-position/match&loop)
同样,这是与原始算法相同的算法。但是我会发现很快就能轻松搞定。
答案 1 :(得分:2)
名为let 的是Scheme中常见的习语:
(define (min-position xs)
(let loop ((xs xs) (pos 0) (mn #f) (mnpos #f))
(if (null? xs)
mnpos
(let ((c (car xs)))
(if (or (not mn) (< c mn))
(loop (cdr xs) (add1 pos) c pos)
(loop (cdr xs) (add1 pos) mn mnpos))))))
在Racket中,您还可以使用for/fold
和in-indexed
来缩短代码:
(define (min-position xs)
(define-values (mn mnpos)
(for/fold ((mn #f) (mnpos #f)) (((c pos) (in-indexed xs)))
(if (or (not mn) (< c mn))
(values c pos)
(values mn mnpos))))
mnpos)
答案 2 :(得分:2)
嗯,这完全取决于你对优雅的定义。对我来说,一个优雅的解决方案是一个简短,清晰,惯用的解决方案,并使用现有的程序(也就是说,它不会重新发明轮子)。这是我的镜头:
(require srfi/1) ; import `list-index`
(require srfi/26) ; import `cute`
(define (min-position lst)
(and (not (null? lst))
(list-index (cute = (apply min lst) <>) lst)))
以下是它的工作原理:
(apply min lst)
使用内置的min
程序(cute = (apply min lst) <>)
使用cute
创建一个专门的谓词,只要元素等于最小值,它就会返回#t
,确保我们只找到最小值(list-index (cute = (apply min lst) <>) lst)
使用list-index
与上一个谓词一起查找列表中第一个最小元素的索引(and (not (null? lst)) … )
部分用于处理输入列表为空的边缘情况短而甜蜜。唯一的缺点是它遍历输入列表两次,一次用于查找最小元素,另一次用于查找该元素的索引。但这只是一个很小的代价,它仍然是O(n)
解决方案,按预期工作:
(min-position '(9 8 7 6 5))
=> 4
(min-position '(9 8 1 6 5))
=> 2
(min-position '(0 1 2))
=> 0
(min-position '())
=> #f