在Racket中用foldl生成n-gram

时间:2015-05-16 12:57:01

标签: scheme racket fold

所以我在摆弄Racket。从单词列表中递归生成n-gram非常简单:

(define (n-grams-recursive words n)
  (if (< (length words) n)
      '()
      (cons (take words n) (n-grams-recursive (cdr words) n))))

迭代版本也很简单:

(define (n-grams words n)
  (define (iter n-grams remaining-words)
    (if (< (length remaining-words) n)
        n-grams
        (iter (cons (take remaining-words n) n-grams)
              (rest remaining-words))))
  (iter '() words))

但是如何使用折叠功能之一进行迭代?我知道这一定是可能的,但我一直在努力与它挣扎一段时间无济于事。我知道折叠的第一个arg应该是一个带有2个参数的函数;迭代的每个阶段的结果列表和输入列表,并在那里进行采取和处理,但我一直遇到问题,因为有非对... ...

1 个答案:

答案 0 :(得分:1)

以下是for/fold的解决方案:

(define (n-grams/fold words n)
  (for/fold ([n-grams '()] [remaining-words words])                
            ([_ (in-naturals)] ; loop forever
             #:break (< (length remaining-words) n))
    (values (cons (take remaining-words n) n-grams)
            (rest remaining-words))))

这是一个使用foldl

的人
(struct state (n-grams remaining-words) #:transparent)

(define (n-grams/foldl words n)
  (foldl (λ (_ s)
           (match-define (state n-grams remaining-words) s)
           (if (< (length remaining-words) n)
               s
               (state (cons (take remaining-words n) n-grams)
                      (rest remaining-words))))
         (state '() words)
         words))

请注意foldl不适合此问题。最好的解决方案是您原来的解决方案。

foldl的问题在于,您一次只能获得列表中的一个元素(并且您需要n第一个元素)。这意味着您需要跟踪n-gram和剩余单词。为了一次跟踪两件事,上面的解决方案使用了一个结构。

Uddate:如果提前生成了子列表,则解决方案变为:

(define (sublists xs)
  (if (empty? xs)
      '()
      (cons xs (sublists (rest xs)))))

(define (n-grams/foldl2 words n)
  (foldl (λ (remaining-words n-grams)
           (if (< (length remaining-words) n)
               n-grams
               (cons (take remaining-words n)
                     n-grams)))
         '()
         (sublists words)))

(n-grams/foldl2 '(a b c d e) 2)