【发布时间】:2026-02-23 16:30:01
【问题描述】:
我最近一直在玩马尔可夫链,试图从一个大型语料库中生成文本,只是为了看看我得到了什么(其中一些很有趣)。
构建文本生成所需的数据结构的很大一部分是创建n-grams。给定一个小示例文本:“今天是 3 月 6 日星期四”一个示例 n-gram,其中n = 3 将是:
Today is Thursday
is Thursday March
Thursday March the
March the sixth
# skipped lines that have < 3 words because is isn't enough for a 3-gram
根据文本的大小,我的代码生成的 n-gram 列表可能非常大,在某些语言中存在 generator 的概念,其中包含用于制作自定义迭代器的 yield 语句,但 Perl不幸的是不是其中之一。
相反,在 Perl 中,我们可以在词法变量上使用闭包来创建 Iterators,但是我在理解使用它们时真正获得的东西时遇到了一些麻烦。
这是我创建的用于创建 n-gram 的迭代器(假设 n 保存在 $self->order 中):
sub _ngrams {
my ($self, @words) = @_;
return sub {
while(@words) {
my @ngram = @words[0 .. $self->order]; # get $order + 1 words
shift @words; # drop the first word
return @ngram;
}
return; # nothing left to do
};
}
我真的从这段代码效率方面获得了什么好处吗?单词列表仍然完全保存在@words 的内存中。是否有替代实现可以减少我的内存占用?
下面是使用迭代器生成字典的方式:
sub seed {
my $self = shift;
my $ngram_it = $self->_ngrams(split /\s+/, $self->text);
GRAM:
while (my @gram = $ngram_it->()) {
next GRAM unless @gram == scalar grep { $_ } @gram;
my $val = pop @gram;
my $key = join ' ', @gram;
if (exists $self->lexicon->{$key}) {
push @{$self->lexicon->{$key}}, $val;
}
else {
$self->lexicon->{$key} = [$val];
}
}
}
任何输入都会非常有帮助。
【问题讨论】:
-
使用迭代器为您提供了灵活性。您可以轻松地交换一个从流中提供单词的迭代器。 (我不会有一个返回 n-gram 的迭代器,我会有一个返回单词的迭代器。)
-
@ikegami 但这在这种情况下可行吗?我需要在哪里获得 N + 1 个单词,然后只删除第一个?然后抓取接下来的 N + 1 个单词,现在包括前面的 N 个单词。
-
只使用已有的逻辑,但将其移出迭代器。