phorth---(その1.6)積み残したrollの実装
rollの実装
保留していたrollに戻ります.
rollはn個の要素をk回だけ「上に」巡回させます.例えば,
10 20 30 40 50 3 1 roll => 10 20 50 30 40 10 20 30 40 50 3 2 roll => 10 20 40 50 30 10 20 30 40 50 3 3 roll => 10 20 30 40 50
という具合です.大きなkに関してはmod nで考えることになります.kが負の数の場合は「下に」巡回します.例えば
10 20 30 40 50 3 -1 roll => 10 20 40 50 30 10 20 30 40 50 3 -2 roll => 10 20 50 30 40 10 20 30 40 50 3 -3 roll => 10 20 30 40 50
となります.このとき小さな負の値(絶対値が大きい負の値)の場合は,kの絶対値のmod nにkの符号をつけたもので考えます.例えば-13だったら,-13 mod 3 = 2ですがそうはせず,13 mod 3=1 として,sign(-13)*1=-1と同じと考えます.
話を単純化します.リストの要素のうち,最初のn個のものを1個だけ上に動かす関数は
roll n xs = (drop 1 $ take n xs) ++ [head xs] ++ (drop n xs)
と表せます.これを再帰的に書き換えて
roll n k xs = case k of k | n < k -> roll n (k mod n) xs | 1 < k && k <= n -> roll n (k-1) $ roll n 1 xs | k == 1 -> (drop 1 $ take n xs) ++ [head xs] ++ (drop n xs) | k == 0 -> xs
これでk>=0のリスト版rollができました.k<0のときも同様に処理します.リストの要素のうち,最初のn個のものを1個だけ下に動かす関数は,同様に考えて
roll' n k xs = case k of k | n < k -> roll' n (k `mod` n) xs | 1 < k && k <= n -> roll' n (k-1) $ roll' n 1 xs | k == 1 -> [last $ take n xs] ++ (take (n-1) xs) ++ (drop n xs)
です.これらをあわせて
roll::Int->Int->[a]->[a] roll n k xs = case k of k | k < 0 -> roll' n (abs k) xs | n < k -> roll n (k `mod` n) xs | 1 < k && k <= n -> roll n (k-1) $ roll n 1 xs | k == 1 -> (drop 1 $ take n xs) ++ [head xs] ++ (drop n xs) | k == 0 -> xs where roll' n k xs = case k of k | n < k -> roll' n (k `mod` n) xs | 1 < k && k <= n -> roll' n (k-1) $ roll' n 1 xs | k == 1 -> [last $ take n xs] ++ (take (n-1) xs) ++ (drop n xs)
とすることで,リストに対するrollが定義できます.
この関数rollをprocStackに与えることでオペレータrollを定めます.IntegerとIntの違いに注意です.
procExp i (Invoke "roll") = procStack i (\((Pushed k):(Pushed n):xs)->roll (fromInteger n) (fromInteger k) xs)
これでとりあえず電卓程度には使える程度にオペレータは定めたと思います(まだまだ整理されてないですけど).ここまで手元では155行のソースになってますが,まだ入出力用のシェルができてません.とりあえず動くだけでもシェルがなければいけません.
おまけ
rollの実装中に間違って作った関数swap.
swap::Int->Int->[a]->[a] swap m n xs = map (\(i,x) -> case i of i | i==m -> xs!!n | i==n -> xs!!m _ -> x) $ zip [0,1..] xs
リストの任意の2要素を交換します.たとえば
swap 1 2 [10,20,30,40] = [10,30,20,40] swap 0 3 [10,20,30,40] = [40,20,30,10]
です.