提问人:Brayw 提问时间:2/1/2022 更新时间:2/1/2022 访问量:95
为什么 ruby 的 IO readlines 方法在后面跟着过滤器时行为不同
Why does ruby's IO readlines method behave differently when followed by a filter
问:
我正在构建一个受 Wordle 启发的小项目,并从我当地的词典中收集单词。最初我是这样做的:
word_list = File.readlines("/usr/share/dict/words", chomp: true)
word_list.filter { |word| word.length == 5 }.map(&:upcase)
第一行绝对需要很长时间。但是,在执行此操作时:
word_list = File.readlines("/usr/share/dict/words", chomp: true).filter { |word| word.length == 5 }.map(&:upcase)
它在几秒钟内完成。在为它们分配内存之前,我无法弄清楚该块是如何应用于正在读取的行的(我假设这是导致读取时间慢的原因),显然每个方法在调用下一个方法之前都没有完全应用,但这就是我认为方法链接的工作方式。filter
答:
0赞
Cary Swoveland
2/1/2022
#1
让我们创建一个文件。
File.write('t', "dog horse\npig porcupine\nowl zebra\n") #=> 34
然后
a = File.readlines("t", chomp:true)
#=> ["dog horse", "pig porcupine", "owl zebra"]
因此,您的块变量包含一个由两个单词组成的字符串。这显然不是你想要的。word
您可以使用 IO::read 将文件“吞入”到字符串中。
s = File.read("t")
#=> "dog horse\npig porcupine\nowl zebra\n"
然后
a = s.scan(/\w+/)
#=> ["dog", "horse", "pig", "porcupine", "owl", "zebra"].
b = a.select { |word| word.size == 5 }
#=> ["horse", "zebra"]
c = b.map(&:upcase)
#=> ["HORSE", "ZEBRA"]
我们当然可以将这些操作串联起来:
File.read("t").scan(/\w+/).select { |word| word.size == 5 }.map(&:upcase)
#=> ["HORSE", "ZEBRA"]
scan(/\w+/)
匹配每个单词字符串(字母、数字和下划线)。若要仅匹配字母,请将其更改为 。scan(/[a-zA-Z]+/)
您可以使用 IO#readlines,它将行读入数组,方法是提取每行的单词,过滤生成的数组以保留具有 5 个字符的数组,然后在大小写后将这些单词添加到先前定义的空数组中。
File.readlines('t')
.each_with_object([]) { |line,arr| line.scan(/\w+/) }
.select { |word| word.size == 5 }
.map(&:upcase)
.each { |word| arr << word } #=> ["HORSE", "ZEBRA"]
您可以将可选参数添加到 的参数中,但没有理由这样做。chomp: true
readline
更好的做法是使用 IO#foreach,它不带块,返回一个可以链接的枚举器,避免使用 创建的临时数组。readlines
File.foreach('t').with_object([]) do |line,arr|
line.scan(/\w+/)
.select { |word| word.size == 5 }
.map(&:upcase)
.each { |word| arr << word }
end
#=> ["HORSE", "ZEBRA"]
评论
, "abaciscus", "abacist"...