检查整数列表是否在升序

时间:2016-11-09 23:00:02

标签: functional-programming lisp racket typed-racket

我正在尝试编写一个函数来检查整数列表是否严格提升。我有以下内容:

(: ascending : (Listof Integer) -> Boolean)
;; test whether a list of integers is *strictly* ascending
(define (ascending x)
  (match x
    ('() #f)
    ((cons hd tl)
     (cond
       ((< hd (second x)) #t)
       ((> hd (second x)) #f)
       (else (ascending tl)))))) 

它适用于前三个检查,但不适用于下面给出的最后三个检查:

(check-expect (ascending (list 1 2 3 4 5)) #t)
(check-expect (ascending (list 5 4 3 2 1)) #f)
(check-expect (ascending (list 5 1 2 3 4)) #f)
(check-expect (ascending (list 1 2 3 5 4)) #f)
(check-expect (ascending (list 1 3 2 4 5)) #f)
(check-expect (ascending (list 1 2 3 4 1)) #f)

我不明白我做错了什么,因为我必须做以下事情:

  1. 检查列表是否为空并引发错误。完成。蜱。
  2. 如果列表中只有两个元素,请比较hdtl以及hd&lt; tl然后#t,如果不是#f。完成勾选。
  3. 其他对所有整个列表进行两次比较,直到您有值#t#f
  4. 请帮忙。

5 个答案:

答案 0 :(得分:2)

你有一些问题。

首先,您错过了仅包含1个元素的列表的情况,该列表应该是基本情况,并返回#t。否则,您将在仅包含1个元素的列表上使用(second x),这是一个错误。

接下来,当列表包含2个或更多元素时,如果第一个元素小于第二个元素并且列表的尾部也在升序,则它会升序。你只是检查前两个元素。您的else子句仅在第一个和第二个元素相等时运行,因为前两个cond检查处理<>。递归调用不是一个单独的情况,它与前两个元素递增的情况相结合。

(define (ascending x)
  (match x
    ('() #f) ;; Empty list is error
    ((cons hd '()) #t) ;; 1-element is success
    ((cons hd tl)
     (if (< hd (second x))
         (ascending tl)
         #f))))

if也可简化为:

(and (< hd (second x)) (ascending tl))

答案 1 :(得分:1)

由于函数#'<接受任意数量的参数,您只需执行以下操作:

(apply #'< '(1 2 3 4)) => T

答案 2 :(得分:1)

如果您允许使用模式匹配来解决此问题,则可以使用更复杂的模式将问题分解为单独的部分。

(: ascending? : (Listof Integer) -> Boolean :)
(define (ascending? xs)
  (match xs
    [(list)           #f] ;; empty list is not considered ascending
    [(list a)         #t] ;; single-element list is always ascending
    ;; match first two elements (a, b) and remaining elements as c
    ;; a should be less than b, and recurse
    [(list a b c ...) (and (< a b) (ascending? (cons b c)))]))

对我而言,这比'()(cons ...)样式匹配好很多。而且因为我们在第三种情况下使用了更具表现力的模式,它使实际执行的代码非常简单。

(ascending? '())               ;; => #f
(ascending? '(1))              ;; => #t
(ascending? '(1 2))            ;; => #t
(ascending? '(1 2 3 4 5 6 7))  ;; => #t
(ascending? '(1 2 3 1))        ;; => #f

我应该提到,你使用的其他风格模式没有任何问题。我认为当模式一致时,它更容易阅读。因此,如果您想使用带引号的模式,而不是将'()conslist等匹配,请将它们用于每个案例,以便更容易看到您正在处理哪些案件。

这个ascending?过程操作相同,只使用不同风格的模式匹配表达式

(: ascending? : (Listof Integer) -> Boolean :)
(define (ascending? xs)
  (match xs
    [`()               #f]
    [`(,a)             #t]
    [`(,a . (,b . ,c)) (and (< a b) (ascending? (cons b c)))]))

还有一件事:你说......

  

检查列表是否为空并引发错误。完成。剔

返回#f不会引发错误。如果你真的打算在有人通过空列表时提出错误,那么你应该使用error程序

...
(match xs
  [(list) (error 'ascending? "empty list given")]
  ...

答案 3 :(得分:0)

对我而言,有时在问题描述中坚持使用更高级别的抽象是有帮助的,而不是将问题分解为适合编程模式,例如模式匹配。

规范

例如,严格排序的列表可以说有两个数学属性:

  1. 列表已排序(单调)。

  2. 列表长度等于包含所有列表元素的集合的基数。

  3. 实施

    使用更多的数学规范:

     #lang typed/racket
     (: strictly-ordered-list? : (Listof Integer) -> Boolean)
     (define (strictly-ordered-list? xs) 
       (define sorted (sort xs <))
         (and (equal? xs sorted)
           (= (set-count (list->set xs))
              (length sorted))))
    

    讨论

    实现是在我们感兴趣的抽象层写的,列表。它测试列表的属性,并且在迭代和比较元素时不会陷入困境。它可能会以一定的速度进行交易,但通常对于有趣的大小数据,O(n log n)与O(n)不太可能成为瓶颈。

答案 4 :(得分:0)

也可以使用带有set!的迭代for循环,虽然这通常不是首选:

(define (asc? l)
  (define res #t)
  (for ((i (sub1 (length l)))
        #:when (not(< (list-ref l i)
                      (list-ref l (add1 i)))))
    (set! res #f)
    )
  res)

测试:

(asc? (list 1 2 3 4 5))

(asc? (list 1 2 3 4 5 4))

输出:

#t
#f