Rangeとeach
を使用して逆方向に反復しようとしました:
(4..0).each do |i|
puts i
end
==> 4..0
0..4
を反復処理すると、数値が書き込まれます。他の範囲r = 4..0
は問題ないようです、r.first == 4
、r.last == 0
。
上記のコンストラクトが予期した結果を生成しないことは、私には奇妙に思えます。その理由は何ですか?この動作が合理的である状況は何ですか?
範囲とは、コンテンツではなく、開始と終了によって定義されるものです。ある範囲での「反復」は、一般的なケースでは実際には意味がありません。たとえば、2つの日付によって生成される範囲で「反復」する方法を考えてください。日ごとに繰り返しますか?月ごと?年ごと?週ごと?明確に定義されていません。 IMO、前方範囲に許可されているという事実は、便利な方法としてのみ表示されるべきです。
そのような範囲で逆方向に反復したい場合は、常にdownto
を使用できます。
$ r = 10..6
=> 10..6
$ (r.first).downto(r.last).each { |i| puts i }
10
9
8
7
6
他の人からの、より多くの考え は、反復を許可し、逆方向範囲を一貫して処理するのが難しい理由についてです。
(0..1).reverse_each
範囲を逆方向に繰り返しますか?
Rubyでeach
を使用して範囲を反復処理すると、範囲内の最初のオブジェクトでsucc
メソッドが呼び出されます。
$ 4.succ
=> 5
また、5は範囲外です。
このハックで逆反復をシミュレートできます:
(-4..0).each { |n| puts n.abs }
ジョンは、これが0にまたがると機能しないことを指摘しました。
>> (-2..2).each { |n| puts -n }
2
1
0
-1
-2
=> -2..2
彼らが意図を曖昧にしているので、私はそれらのどれも本当に好きだと言うことはできません。
「プログラミングRuby」の本によると、Rangeオブジェクトは範囲の2つのエンドポイントを保存し、.succ
メンバーを使用して中間値を生成します。範囲で使用しているデータ型の種類に応じて、Integer
のサブクラスを常に作成し、.succ
メンバーを再定義して、逆イテレーターのように機能させることができます(おそらく.next
も再定義したい)。
範囲を使用せずに、探している結果を達成することもできます。これを試して:
4.step(0, -1) do |i|
puts i
end
これは、-1のステップで4から0までステップします。ただし、これが整数引数以外で機能するかどうかはわかりません。
別の方法は(1..10).to_a.reverse
for
ループを使用することもできます。
for n in 4.downto(0) do
print n
end
印刷するもの:
4
3
2
1
0
リストがそれほど大きくない場合。おもう [*0..4].reverse.each { |i| puts i }
は最も簡単な方法です。
Btaが言ったように、その理由はRange#each
は、succ
を最初に送信し、次にそのsucc
呼び出しの結果に送信し、結果が終了値より大きくなるまで続けます。 succ
を呼び出して4から0を取得することはできません。実際には、すでに終了よりも大きく開始しています。
逆範囲での反復を実現する方法をもう1つ追加します。使用していませんが、可能性があります。モンキーパッチRubyコアオブジェクト。
class Range
def each(&block)
direction = (first<=last ? 1 : -1)
i = first
not_reached_the_end = if first<=last
lambda {|i| i<=last}
else
lambda {|i| i>=last}
end
while not_reached_the_end.call(i)
yield i
i += direction
end
end
end
OPが書いた
上記のコンストラクトが予期した結果を生成しないことは、私には奇妙に思えます。その理由は何ですか?この動作が合理的である状況は何ですか?
「できますか?」しかし、実際に尋ねられた質問に到達する前に尋ねられなかった質問に答えるために:
$ irb
2.1.5 :001 > (0..4)
=> 0..4
2.1.5 :002 > (0..4).each { |i| puts i }
0
1
2
3
4
=> 0..4
2.1.5 :003 > (4..0).each { |i| puts i }
=> 4..0
2.1.5 :007 > (0..4).reverse_each { |i| puts i }
4
3
2
1
0
=> 0..4
2.1.5 :009 > 4.downto(0).each { |i| puts i }
4
3
2
1
0
=> 4
Reverse_eachは配列全体を構築すると主張されているため、downtoの方が明らかに効率的です。言語設計者が、そのようなものを実装することを検討することさえできるという事実は、尋ねられた実際の質問への答えに結びついています。
実際に尋ねられた質問に答えるには...
その理由は、Rubyは無限に驚くべき言語です。いくつかの驚きは楽しいですが、まったく壊れている多くの動作があります。これらの以下の例のいくつかは新しいリリースで修正されても他にもたくさんありますが、それらは元のデザインの考え方に対する起訴として残っています。
nil.to_s
.to_s
.inspect
結果は「」ですが、
nil.to_s
# .to_s # Don't want this one for now
.inspect
結果として
syntax error, unexpected '.', expecting end-of-input
.inspect
^
おそらく、配列への追加に関して<<とPushが同じであると期待するでしょうが、
a = []
a << *[:A, :B] # is illegal but
a.Push *[:A, :B] # isn't.
「grep」は、Unixのコマンドラインと同じように動作するはずですが、名前にかかわらず、===ではなく、=〜に一致します。
$ echo foo | grep .
foo
$ Ruby -le 'p ["foo"].grep(".")'
[]
さまざまなメソッドは予想外に互いのエイリアスであるため、同じことに対して複数の名前を覚える必要があります。 find
およびdetect
-ほとんどの開発者が好きで、どちらか一方のみを使用する場合でも。 size
、count
、およびlength
についてもほぼ同じです。ただし、それぞれ異なる定義を行うクラス、または1つまたは2つをまったく定義しないクラスを除きます。
他の何かを実装していない限り-コアメソッドtap
のようなものは、画面上の何かを押すためにさまざまな自動化ライブラリで再定義されています。特に、他のモジュールが必要とするモジュールが文書化されていない何かを行うためにさらに別のモジュールに依存している場合、何が起こっているのかを見つけてください。
環境変数オブジェクトENVは「マージ」をサポートしていないため、次のように記述する必要があります。
ENV.to_h.merge('a': '1')
ボーナスとして、あなたが彼らがどうあるべきかについて考えを変えるなら、あなたまたはあなたの誰かの定数を再定義することさえできます。
これは私の怠zyなユースケースでうまくいきました
(-999999..0).lazy.map{|x| -x}.first(3)
#=> [999999, 999998, 999997]