Le problème est que vous aussi itérez votre a
qui est en fait un hachage entre les balises et les mots.
Si vous traitez votre scan
par un hash
au lieu d'un array
, vous n'obtenez pas de doublons.
RE = /<("[^"]*"|'[^']*'|[^'">])*>/
TAG_RE = /<(.+?)>(.*?)<.+?>/
text = "<date>show</date> me the current conditions for <city> detroit <END>"
a = text.scan(TAG_RE)
text.gsub(RE, '').split.each do |q|
d = a.find { |p| p.last.strip == q }
if d
puts "#{q}\tB-#{d.first}"
else
puts "#{q}\tO"
end
end
Sortie:
show B-date
me O
the O
current O
conditions O
for O
detroit B-city
Et, pendant que nous y sommes, vous pouvez utiliser un bon hash
:
RE = /<("[^"]*"|'[^']*'|[^'">])*>/
TAG_RE = /<(.+?)>(.*?)<.+?>/
text = "<date>show</date> me the current conditions for <city> detroit <END>"
map = Hash[*text.scan(TAG_RE).flatten.map(&:strip)].invert
text.gsub(RE, '').split.each do |q|
tag = map[q]
if tag
puts "#{q}\tB-#{tag}"
else
puts "#{q}\tO"
end
end
qui génère la même sortie.
EDIT: Si vous vous demandez sur un plus Ruby- esque façon, je ferais probablement quelque chose comme ceci:
class Text
TAGS_RE = /<("[^"]*"|'[^']*'|[^'">])*>/
TAGS_WORDS_RE = /<(.+?)>\s*(.*?)\s*<.+?>/
def self.strip_tags(text)
text.gsub(TAGS_RE, '')
end
def self.tagged_words(text)
matches = text.scan(TAGS_WORDS_RE)
Hash[*matches.flatten].invert
end
end
class Word
def self.display(word, tag)
puts "#{word}\t#{Word.tag(tag)}"
end
private
def self.tag(tag)
tag ? "B-#{tag}" : "0"
end
end
text = "<date>show</date> me the current conditions for <city> detroit <END>"
words_tag = Text.tagged_words(text)
Text.strip_tags(text).split.each do |word|
tag = words_tag[word]
Word.display(word, tag)
end
Pourquoi? Je ne suis pas très intelligent et je suis très paresseux, donc je préfère écrire des choses aussi explicites que possible. Donc, j'essaie d'éviter autant que possible les cycles.
L'écriture d'une boucle est facile, mais la lecture d'une boucle n'est pas aussi simple car vous devez garder le contexte de ce que vous lisez pendant que vous continuez à lire et à analyser le code source.
Habituellement, les cycles avec break
s et next
s sont encore plus difficiles à analyser, car vous devez garder une trace sur les chemins de code qui terminent brusquement le cycle.
Les cycles imbriqués sont encore plus difficiles car vous devez garder la trace de plus d'un contexte qui change à différentes vitesses. Je crois que la version proposée est plus facile à lire parce que chaque ligne peut être comprise par elle-même. Il y a très peu de contexte dont nous devons nous souvenir en passant d'une ligne à l'autre.
Les détails sont résumés dans les méthodes, donc si vous voulez juste comprendre la situation, vous pouvez regarder la partie principale du code:
words_tag = Text.tagged_words(text)
Text.strip_tags(text).split.each do |word|
tag = words_tag[word]
Word.display(word, tag)
end
Et si vous voulez comprendre les détails sur la façon dont il est fait, vous regardez comment les méthodes sont mises en œuvre. Avec cette approche, les détails de mise en œuvre ne sont pas divulgués aux endroits où ils pourraient ne pas être nécessaires. Je pense que c'est une bonne pratique dans tous les langages de programmation, pas seulement dans Ruby.
Est-ce que detroit doit avoir une balise fermante ''? –
Cela ne compte pas. Il suffit de vérifier les mots inclus dans les balises, puis obtient le nom de l'étiquette de la partie initiale. – arjun