2017-04-15 16 views
1

このコード出力の重複をどのように停止できますか?Rubyループ出力重複

RE = /<("[^"]*"|'[^']*'|[^'">])*>/ 
TAG_RE = /<(.+?)>(.*?)<.+?>/ 

text = "<date>show</date> me the current conditions for <city> detroit <END>" 
a = [] 

text.scan(TAG_RE).map { |w| a<< w; } 

text.gsub(RE, '').split.each do |q| 
    a.each_with_index do |v, i| 
     if q == a[i].last.strip 
      puts "#{q}\tB-#{a[i].first}"   
     else 
      puts "#{q}\tO"   
     end 

    end 
end 

OUTPUTS

show B-date 
show O 
me O 
me O 
the O 
the O 
current O 
current O 
conditions O 
conditions O 
for O 
for O 
detroit O 
detroit B-city 

彼らは私がループでnextを置くことができ、この

show B-date 
me O 
the O 
current O 
conditions O 
for O 
detroit B-city 

同様の条件

に一致する場合、私は言葉だけの単一のインスタンスをしたいですか?

編集
このコードはRubyioticですか?

text.gsub(RE, '').split.each do |q| 
    a.each_with_index do |v, i| 
     @a = a[i].last.strip # save in a variable  
     if @a == q 
      puts "#{q}\tB-#{a[i].first}"  
      break # break inner loop if match found 
     end 
    end 
    next if @a == q #skip current outer loop if match found 
    puts "#{q}\tO" 
end 
+0

デトロイトには「」というタグがありますか? –

+0

それは問題ではありません。タグ内に囲まれた単語をチェックし、最初の部分からタグ名を取得するだけです。 – arjun

答えて

2

問題はあなたにも、実際にタグと単語間のハッシュである、あなたのaを反復処理していることです。

scanarrayの代わりにhashと指定すると、重複はありません。

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 

出力:

show B-date 
me  O 
the  O 
current O 
conditions  O 
for  O 
detroit B-city 

そして、我々はそれでいる間、あなたは適切な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 

EDIT:

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 

なぜ:あなたはもっとRuby- 道に迷っている場合は 、私はおそらくこのような何かをしますか?

私はそれほど賢くないと私は非常に怠惰なので、私は可能な限り明示的に物事を書くことを好む。だから、できるだけサイクルを避けようとしています。

ループを書くのは簡単ですが、ループを読み込むのは簡単ではありません。なぜなら、ソースコードの読み取りと解析を続けている間は、読み込んだ内容のコンテキストを維持する必要があるからです。

通常、どのコードパスがサイクルを突然終了させるかを把握する必要があるため、breaksおよびnextのサイクルは、さらに解析するのが難しいです。

異なる速度で変化する複数のコンテキストを追跡する必要があるため、ネストされたサイクルはさらに困難です。

提案されたバージョンは、各行がそれ自身のものであると理解できるので、読みやすいと思います。ある行から次の行に行く間に覚えなければならないコンテキストはほとんどありません。

詳細は、メソッドに抽象化されているので、あなただけの全体像を把握したい場合は、コードの主要な部分を見ることができます:

words_tag = Text.tagged_words(text) 
Text.strip_tags(text).split.each do |word| 
    tag = words_tag[word] 
    Word.display(word, tag) 
end 

そして、あなたはそれだ方法についての詳細を理解したい場合完了したら、メソッドがどのように実装されているかを調べます。このアプローチでは、実装の詳細が必要でないかもしれない場所に漏洩することはありません。

これはRubyだけでなく、すべてのプログラミング言語で良い方法であると思います。

+0

Heya。私はその質問を編集した。私は 'break'と' next'を使いました。グッドルビー? _BTW、あなたのコードは良い味があります。もちろん 'ハッシュ'を考えていたはずです;)。 – arjun

+0

ありがとう:)あなたの新しい質問に答えるための答えを更新しました。 – Gaston