gpt4 book ai didi

ruby - 使用功能样式匹配流中任意数量的以下标记

转载 作者:数据小太阳 更新时间:2023-10-29 08:06:59 24 4
gpt4 key购买 nike

问题如下:

  1. 有一个带有标记的文件 - 每个标记都在一个单独的行中,并附有一些元数据(例如文档 ID),
  2. 应该计算一些标记序列,一个序列可能是一个或多个标记,
  3. 序列保存在 trie 中,但这不是必需的,
  4. 实现必须非常高效,因为要处理的文件有数 GB 的数据。

我目前的实现(在 Ruby 中)如下:

def convert_tuple(tuple)
document_id, token_index, space, token = *tuple
token = token.chomp
token.force_encoding("ascii-8bit")
document_id = document_id.to_i
[document_id, token_index, space, token]
end

def count_and_match_tokens(string, index, counts, document_id, first_token_index, last_token_index)
token_id = index[string]
if token_id
STDERR.puts "%s\t%s\t%s\t%s" % [document_id, first_token_index, last_token_index, string]
counts[string] += 1
end
index.search(string).size > 0
end

counts = Hash.new(0)
index = Melisa::IntTrie.new
index.load(index_path)

CSV.open(input_path, col_sep: "\t") do |input|
input.each do |tuple|
document_id, first_token_index, space, token = convert_tuple(tuple)
recoreded_pos = input.pos
last_token_index = first_token_index
string = token.dup
while(count_and_match_tokens(string, index, counts, document_id, first_token_index, last_token_index)) do
last_document_id, last_token_index, space, last_token = convert_tuple(input.shift)
break if document_id != last_document_id
string << " " if space == "1"
string << last_token
end
input.pos = recoreded_pos
end
end

CSV.open(output_path,"w") do |output|
counts.each do |tuple|
output << tuple
end
end

convert_tuple 函数仅对数据进行基本转换(即将字符串转换为数字等)。

count_and_match_tokens 函数计算标记并返回 true,如果传递的字符串参数是不同字符串的前缀。我使用一个 trie 结构来有效地验证这个条件。

我想知道使用函数式风格编写的解决方案会是什么样子。我面临的问题是匹配的序列可能跨越许多标记。

在 Ruby 中(或一般的 OO 风格)我可以记录开始匹配的位置 (recorded_pos = input.pos) 并“重置”流,当子序列匹配时结束了(input.pos = recorded_pos)。因此,对 each 的后续调用将返回流中的下一个标记。因此,已识别序列内的标记(在 while 循环内处理的标记)也可以是其他子序列中的第一个匹配标记。

我会感谢 Elixir 中的解决方案,但任何其他函数式语言也可以。

编辑

我提供了 convert_tuplecount_and_match_tokens 的定义以及示例输入和输出(文件被截断,因此计数不直接对应于输入文件).

代码中出现的索引数据结构是一个Maris Trie (Melisa gem: https://github.com/wordtreefoundation/melisa/)

示例输入:

0   746 1   The
0 748 1 river
0 751 1 Bosna
0 754 1 (
0 763 0 )
0 765 1 (
0 766 0 Cyrillic
0 767 0 :
0 769 1 Босна
0 770 0 )
0 772 1 is
0 774 1 the
0 776 1 third
0 778 1 longest
0 781 1 river
0 784 1 in
0 787 1 Bosnia
0 789 1 and
0 791 1 Herzegovina
0 793 0 ,
0 795 1 and
0 797 1 is
0 799 1 considered
0 801 1 one
0 803 1 of
0 805 1 the
0 807 1 country
0 808 0 '
0 809 0 s
0 811 1 three
0 813 1 major
0 815 1 internal
0 817 1 rivers

要识别的 token 序列:

Bosnia
Bosnia and Herzegovina
river
Herzegovina

示例输出:

river,2
Bosnia,1
Bosnia and Herzegovina,1
Herzegovina,1

我希望这有助于理解我要解决的问题。

最佳答案

一个可运行的程序(count_sequences.rb):

#!/usr/bin/env ruby
require 'set'

sequence_file, token_file = ARGV

sequences = Set.new

forest = File.readlines(sequence_file).each{|s| sequences << s.tap(&:chomp!)}.map!(&:split).each_with_object({}) do |words, root|
words.reduce(root) do |parent, word|
(parent[word] ||= [0, {}])[1]
end
end
#=> {
# "Bosnia" => [0, {
# "and" => [0, {
# "Herzegovina" => [0, {}]
# }]
# }],
# "river" => [0, {}]
# }

File.open(token_file) do |f|
current_node = forest

f.each_line do |line|
token = line.tap(&:chomp!).split[-1]
spec = current_node[token] || forest[token]
if spec
spec[0] += 1
current_node = spec[1]
else
current_node = forest
end
end
end
#=> {
# "Bosnia" => [1, {
# "and" => [1, {
# "Herzegovina" => [1, {}]
# }]
# }],
# "river" => [2, {}]
# }

def print_tree(node, sequences, parent = nil)
node.each do |word, spec|
sequence = [parent, word].compact.join(' ')
puts "#{sequence},#{spec[0]}" if sequences.include? sequence
print_tree(spec[1], sequences, sequence)
end
end

print_tree(forest, sequences)

你可以运行它

$ ruby count_sequences.rb /path/to/sequences.txt /path/to/tokens.txt

输出

Bosnia,1
Bosnia and Herzegovina,1
river,2

关于ruby - 使用功能样式匹配流中任意数量的以下标记,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44364424/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com