1

この例でgrepがどのように機能するかを理解しようとしています。コードは機能しますが、イベントがどの順序で発生するか、いつどこで何が返されるかを正しく理解しているかどうかは 100% わかりません。

cars = [:Ford, :Toyota, :Audi, :Honda]
ucased_cars = cars.collect do |c| 
c.to_s 
end
.grep(/^Ford/) do |car| 
puts car.upcase 
car.upcase 
end
puts "ucased:" + ucased_cars.to_s

私が考えていることは次のとおりです。

  1. シンボルの配列を定義します
  2. ブロック内で、cars 配列の各 Symbol 要素 c を String に変換するブロックで collect メソッドを呼び出します。
  3. collect は文字列の配列を返します
  4. grep は、collect によって返された文字列の配列に対して呼び出され、grep は検索パターンに一致する各配列要素 car に対して独自のブロックを呼び出し、要素を出力して大文字にし、配列の一部として返します。
  5. grep は大文字の文字列の配列を返し、それを「ucased_cars」に割り当てます
  6. 配列 ucased_cars は、出力する前に文字列に変換する必要があります。

ステップ 4 に関する限り、grep の動作を最もよく表しているのは次のうちどれですか。

[A] grep は、パターンに一致するすべての文字列を検索します。grep は、この一致の配列でブロックを呼び出します。grep は、ブロックの結果を呼び出し元の関数に返します。

[B] grep は、パターンに一致する最初の文字列を見つけます。grep は、この一致でブロックを呼び出します。このブロックの戻り値は、一時的にどこかに積み上げられます。grep は、配列の次の要素を検索します。一致する場合、grep はこの一致のブロックを呼び出します。grep は、このブロックの戻り値を戻り値の一時的な「ストレージ」に追加します。grep は、一致するものがなくなるまで、次の配列要素を調べます。次に、grep は積み上げられた戻り値を呼び出し元の関数に渡します。

私の結論:

[A] より理にかなっているようです。

[B] 不必要なごまかしが多く、効率的でもありそうもない。

4

1 に答える 1

12

まず、grep のドキュメントは次のとおりです。

コードをクリーンアップして、少しずつ説明させてください

# 1
cars = [:Ford, :Toyota, :Audi, :Honda]

# 2
ucased_cars = cars.collect do |c| 
  c.to_s
end.grep(/^Ford/) do |car|  # 3
  puts car.upcase # 4
  car.upcase # 5
end
# 6

# 7
puts "ucased:" + ucased_cars.to_s
  1. シンボルの配列を宣言する

  2. collect を使用してシンボルを文字列に変換します。あなたが得る["Ford", "Toyota", "Audi", "Honda"]

  3. この文字列の配列を grep に入力します。正規表現に一致するアイテムはすべて/^Ford/ブロックに供給されます

  4. ブロックは、フィードされた大文字の文字列を出力します

  5. ブロックは大文字化された文字列を返し、grep はそれを「一致値」として取得します。

  6. grep からの戻り値 (すべての「一致値」の配列) が に割り当てられucased_carsます["FORD"]

  7. その後、印刷されます。配列に対して a を実行するto_sと、このように一緒に詰まったすべての要素が出力されます。これはあまり役に立ちません。印刷したほうがよいでしょうucased_cars.inspect

grep が舞台裏でどのように機能するかについての質問に答えるには...

上記のドキュメント ページには、grep 自体の C ソースが示されています。基本的にこれを行います:

  • 新しい ruby​​ 配列を割り当てます (動的にサイズ変更されます)
  • を呼び出しrb_iterateて、ソース内の各要素をウォークスルーし、grep 固有のコードを渡します。
  • rb_iteratecollecteach_with_indexおよび他の多くのものによっても使用されます。

collect/each/etc がすべてどのように機能するかを知っているので、ソース コードでこれ以上詳しく調べる必要はありません。答えはあります。それがあなたの [B] です。

さらに詳しく説明すると、次のようになります。

  1. 戻り値を保持する新しい配列を作成します。
  2. ソースから次のアイテムを取得する
  3. 正規表現と一致する場合:
    • ブロックが指定されている場合は、ブロックを呼び出し、ブロックが返すものは何でも戻り値に入れます。
    • ブロックが指定されていない場合は、アイテムを戻り値に入れます
  4. 2 に進み、ソースに項目がなくなるまで繰り返します。

「Aの方がずっと理にかなっているようだ」というあなたのコメントについては、同意しません。

アイデアは、ブロックが要素で何かをするということです。最初にソースをスキャンしてから、一致する配列をブロックに渡すと、ブロックは自分each自身を呼び出す必要があり、面倒です。

第二に、効率が低下します。たとえば、ブロックが呼び出しreturnまたはエラーを発生させた場合はどうなりますか? 現在の化身では、ソースの残りの部分をスキャンする必要がなくなります。ソース リスト全体を事前にスキャンしていた場合は、このすべての労力を無駄にしていたでしょう。

于 2009-03-22T19:56:30.193 に答える