2011/12/17
範囲の指定
Schemeでは「ここからここまで」という範囲を指定するのに、
始点(始点自身は含まれる)と終点(終点自身は含まれない)のhalf-open intervalを
使うことが多い。SRFI:13(string library)やSRFI:43(vector library)
などの代表的なSRFIがそうだし、Gaucheもそれに合わせてる。これを [始点,終点)
と書くことにしよう。
この他に代表的な流儀としては、始点と終点をともに含まれるとするclosed interval、
それから始点と長さがある。それぞれ [始点,終点]
, {始点,長さ}
としよう。
SchemeではSRFI:1のiotaが例外的に{始点,長さ}
を取る。但し引数順としては長さの方が先で、始点は省略可能、省略時0という形。3番目の引数としてステップを取ることもできる。
Pythonのarray slicingは[始点,終点)
、オプショナルでステップ(ストライド)。
Common Lispのloop
マクロは、[始点,終点]
と[始点,終点)
のどちらも使える。前者はfrom〜toとかfrom〜downto、後者はfrom〜belowとfrom〜above。
Rubyはどれも使える。配列でx[a,b]とやれば{始点,長さ}
, さらにrangeオペレータが2種類あって[始点,終点]
と[始点,終点)
に対応。
この3方式は容易に相互変換可能なので、どれを採用するかは好みの問題、一貫性があればいいや、と今までは思っていた。
ところが、とあるAPIを考えていて、2つのパラメータで範囲指定させ、かつ昇順と降順の区別も出来たらいいな、と考えてたら、この3方式に表現力の違いがあることに気づいた。正確には、インデックスのround trip (-1, -2 など負のインデックスで、終端からの距離を数える場合。例えばpythonでa[0:-1]とやれば最後の要素を除いた配列が得られる、など) を使おうとした場合、だけれど。
{始点,長さ}
は「長さが負の場合に降順とする」というルールを入れればすぐ対応できる。インデックスのround tripも特に問題ない。但し、「最後から3番目の要素を起点に最初の要素までを降順で」のように取りたい時に、範囲の長さを陽に与える必要がある (昇順なら、「長さ省略時が最後の要素まで」としておけるのに)。対称でないのはちょっと気持ちわるい。[始点,終点]
では 始点>終点 の時に降順とすることをすぐに思いつくが、round tripがあると単純な大小比較ができない。始点と終点をそれぞれmodulo(範囲の大きさ)した後で大小比較すれば何とかなる。若干、直感的でない場合は出てくる。[始点,終点)
が困りもの。ある値から逆順に先頭までを入れたい時には、「ゼロより一つ前」を終点として指定する必要がある。これを-1にしてしまうと、「終端からひとつ手前」を指定するのと区別がつかない。言い換えれば、この方式では「一番最後の要素+1」と「一番最初の要素-1」を別々に表現する必要があって、その分表記にバリエーションが必要になるということだ。end省略時に「最後の要素+1を指す」とする場合は多いが、「省略された」というのは1種類の情報でしかないので、降順を表現するのに別の情報が必要になる。
Luaみたいにインデックスを1からスタートすることにすれば0を「最初の要素のひとつ前」とすることが出来る。この点では1-ベースのインデクシングに利点があるとも言えるなあ。
Tag: Programming
Post a comment