Racket 中 3 种检测 true 方法的区别

Difference between 3 methods of detecting true in Racket

这看起来确实很重要,但是以下3种统计真实项目数的方法有什么显着差异吗?

#lang racket

(define (counttrue . vars)
  (length (remove* (list #f) vars))    )

(define (counttrue2 . vars)
  (define c 0)
  (for ((item vars))
    (when item (set! c (add1 c))))
  c    )

(define (counttrue3 . vars)
  (count (lambda(x) x) vars)    )

它们都产生相同的结果,但是有什么理由可以或不应该选择某个特定的结果吗?

编辑:在使用时间函数时,使用上面的 3 个函数和@ChrisJester-Young 和@Sylwester 的回答各有 2 个得到以下结果:

"---------- counttrue ------------"
cpu time: 751 real time: 751 gc time: 16
"---------- counttrue2 ------------"
cpu time: 946 real time: 947 gc time: 10
"---------- counttrue3 ------------"
cpu time: 456 real time: 457 gc time: 8
"---------- counttrue_chris1 ------------"
cpu time: 726 real time: 727 gc time: 9
"---------- counttrue_chris2 ------------"
cpu time: 595 real time: 595 gc time: 8
"---------- counttrue_sylwester1 ------------"
cpu time: 543 real time: 544 gc time: 7
"---------- counttrue_sylwester2 ------------"
cpu time: 515 real time: 515 gc time: 7

因此,"count lambda" 方法是最快的。

count 版本可能是最惯用的(除了我会把它写成 (count identity items))。此外,set! 版本绝对不是惯用的 Racket,并且 Racket 没有优化它的使用,正如您从计时测试中看到的那样。

这里有几个供您选择的时间选择:

  1. 使用for推导式:

    (for/sum ((x (in-list items)) #:when x) 1)
    
  2. 手动循环:

    (let loop ((sum 0)
               (items items))
      (cond ((null? items) sum)
            ((car items) (loop (add1 sum) (cdr items)))
            (else (loop sum (cdr items)))))
    

它们只是做同样事情的不同方式。它们都是 O(n),所以它们在时间上相差不大。他们中的一些人因为有一个中间列表而浪费了一点内存,但我认为你在比较这些列表上浪费的时间比你选择最快的列表要回来的时间要多。根据您的第三个,我会选择最短且最重要的那个:

(define (count-true . args)
  (count values args))

其实就是一个专门的foldl:

(define (count-true . args)
  (foldl (lambda (val sum) 
           (if val (add1 sum) sum)) 
         0 
         args))

countfoldl 都是在 #!racket 中用命名的 let 实现的。