在 Racket 中使用 foldl 生成 n-gram
Generating n-grams with foldl in 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))
但是我如何使用其中一个折叠函数来实现迭代版本?我知道这一定是可能的,但我已经为此苦苦挣扎了一段时间但无济于事。我知道要折叠的第一个参数应该是一个带有 2 个参数的函数;在迭代的每个阶段的结果列表和输入列表,并在那里进行取舍,但我一直 运行 遇到 cons 命中非对的问题...
这里是 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 和剩余单词。为了同时跟踪两件事,上面的解决方案使用结构。
更新:如果提前生成子列表,解决方案变为:
(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)
所以我在摆弄球拍。从单词列表递归生成 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))
但是我如何使用其中一个折叠函数来实现迭代版本?我知道这一定是可能的,但我已经为此苦苦挣扎了一段时间但无济于事。我知道要折叠的第一个参数应该是一个带有 2 个参数的函数;在迭代的每个阶段的结果列表和输入列表,并在那里进行取舍,但我一直 运行 遇到 cons 命中非对的问题...
这里是 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 和剩余单词。为了同时跟踪两件事,上面的解决方案使用结构。
更新:如果提前生成子列表,解决方案变为:
(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)