リストがあり、このリストの他のすべての要素を右から2倍にしたい。
この問題を解決する別の関連する質問がありますが、右ではなく左から倍増します: Haskell: Double every 2nd element in list
たとえば、私のシナリオでは、[1,2,3,4] は [2,2,6,4] になり、その質問では、[1,2,3,4] は [1,4,3 になります。 、8]。
これをどのように実装しますか?
リストがあり、このリストの他のすべての要素を右から2倍にしたい。
この問題を解決する別の関連する質問がありますが、右ではなく左から倍増します: Haskell: Double every 2nd element in list
たとえば、私のシナリオでは、[1,2,3,4] は [2,2,6,4] になり、その質問では、[1,2,3,4] は [1,4,3 になります。 、8]。
これをどのように実装しますか?
一番上の回答は質問を誤解していると思います。タイトルは、OPがリストの右から2番目、4番目などの要素を2倍にしたいことを明確に示しています。Ørjan Johansen の答えは正しいですが、遅いです。これが私のより効率的な解決策です:
doubleFromRight :: [Integer] -> [Integer]
doubleFromRight xs = fst $ foldr (\x (acc, bool) ->
((if bool then 2 * x else x) : acc,
not bool)) ([], False) xs
リストを右から折ります。初期値は、空のリストとブール値を含むタプルです。ブール値は false から始まり、毎回反転します。ブール値が true の場合にのみ、値が 2 倍されます。
考えてみてください。
double = zipWith ($) (cycle [(*2),id])
編集 注意してください。これは実際には私の解決策ではありません。リンクされた投稿の解決策であり(*2)
、id
反転しています。そういうわけで、それはとても些細な修正だったので、考えてみると言いました。
さて、他の回答のようにエレガントでも効率的でもありませんが、読みやすさと基本的な機能の観点から、初心者の観点からこれを書きました(私は1人です)。
これは、右から 2 番目ごとに 2 倍になります。
このスクリプトを使用 するdoubleEveryOther [1,3,6,9,12,15,18]
と[1,6,6,18,12,30,18]
: doubleEveryOther [1,3,6,9,12,15]
[2,3,12,9,24,15]
doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther [] = []
doubleEveryOther (x:[]) = [x]
doubleEveryOther (x:y:zs)
| (length (x:y:zs)) `mod` 2 /= 0 = x : y*2 : doubleEveryOther zs
| otherwise = x*2 : y : doubleEveryOther zs
直接実装は次のようになります。
doubleOddElements :: [Int] -> [Int]
doubleOddElements [] = []
doubleOddElements [x] = [2 * x]
doubleOddElements (x:y:xs) = (2*x):y:(doubleOddElements xs)
私の最初の考えは:
doubleOdd (x:xs) = (2*x):(doubleEven xs)
doubleOdd [] = []
doubleEven (x:xs) = x:(doubleOdd xs)
doubleEven [] = []
関数とシーケンスの長さがより簡単に変更されるという点で、DiegoNolan のソリューションはより洗練されていますが、理解するのに少し時間がかかりました。
右から操作するという要件を追加すると、少し複雑になります。foldr
右から何かを行うためのきちんとした出発点なので、試してみましょう:
doubleOddFromRight = third . foldr builder (id,double,[])
where third (_,_,x) = x
builder x (fx,fy,xs) = (fy, fx, fx x : xs)
double x = 2 * x
これにより、エントリごとにfx
との 2 つの関数が入れ替わります。fy
エントリの値を見つけるには、長さが奇数か偶数かを調べて、リストの最後までたどる必要があります。
私はHaskellを学んでいるので、次の初心者向けソリューションを見つけてください。、、または_ _zipWith
cycle
reverse
doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther [] = []
doubleEveryOther s@(x:xs)
| (length s) `mod` 2 == 0 = (x * 2) : (doubleEveryOther xs)
| otherwise = x : (doubleEveryOther xs)
重要なことは、右からすべての要素を 2 倍にする場合、2 倍を 2 つのケースに分けることができるということです。
CS194の宿題の一部としてこれに答えました
私もCIS 194コースからこの質問に来ています。
私はこれを2つの方法で行いました。最初に、質問のポイントは、リストされている3つの可能なソースのいずれかに記載されている関数またはプログラミングの方法にのみ依存する必要があると考えました。コースの講義 1、Real World Haskell ch. 1,2とHaskell を学ぼう ch. 2 .
じゃあ良いよ:
reverse
max
、 、min
、odd
、などの基本機能even
head
, tail
, ...良くないですよ:
foldr
、foldl
、map
最初の解決策、カウンターで再帰を使用するだけです:
doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther xs = loopDoubles xs 1
loopDoubles :: [Integer] -> Integer -> [Integer]
loopDoubles [] _ = []
loopDoubles xs n = loopDoubles (init xs) (n + 1) ++ [doubleEven (last xs) n]
doubleEven :: Integer -> Integer -> Integer
doubleEven x n = if even n then x * 2 else x
このメソッドは再帰を使用しますが、再帰の各レベルでの長さの計算を回避します。
私の上記のルールを破る2番目の方法:
doubleEveryOther' :: [Integer] -> [Integer]
doubleEveryOther' xs = map (\x -> if even (fst x) then (snd x) * 2 else snd x) $ zip (reverse [1..n]) xs
where n = length(xs)
この 2 番目のものは、反転した一連のインデックスを構築し、これらをマッピングすることによって機能します。これは長さを計算しますが、一度だけです。
例えば[1,1,1,1] -> [(4,1),(3,1),(2,1),(1,1)]
これらは両方とも、右から1 つおきの要素を 2 倍にするという要件に従っています。
> doubleEveryOther [1,2,3,4]
[2,2,6,4]
> doubleEveryOther [1,2,3]
[1,4,3]
> doubleEveryOther' [1,2,3,4]
[2,2,6,4]
> doubleEveryOther' [1,2,3]
[1,4,3]
一部の回答は、リストの奇数/偶数の長さを扱っていないようです。
doubleEveryOtherEvenList = zipWith ($) (cycle [(*2),id])
doubleEveryOther :: [Int] -> [Int]
doubleEveryOther n
| length n `mod` 2 == 0 = doubleEveryOtherEvenList n
| otherwise = (head n) : doubleEveryOtherEvenList (tail n)
Haskell CIS194 Courseからの Homework 1 課題への回答を調査しているときに、OP がこの質問を提起したと思います。コースのこの段階では、生徒に Haskell はほとんど教えられていないため、上記の答えは正しいものですが、ラムダ、関数合成 (.)、さらにはライブラリ ルーチンなどの要素があるため、学習中の生徒の理解を超えています。 length と reverse のようなものはまだ導入されていません。コースでの指導の段階に一致する答えは次のとおりです。
doubleEveryOtherEven :: [Integer] -> [Integer]
doubleEveryOtherEven [] = []
doubleEveryOtherEven (x:y:xs) = x*2 : y : doubleEveryOtherEven xs
doubleEveryOtherOdd :: [Integer] -> [Integer]
doubleEveryOtherOdd (x:[]) = [x]
doubleEveryOtherOdd (x:y:xs) = x : y*2 : doubleEveryOtherOdd xs
integerListLen :: [Integer] -> Integer
integerListLen [] = 0
integerListLen (x:xs) = 1 + integerListLen xs
doubleEveryOther :: [Integer] -> [Integer]
doubleEveryOther xs
| integerListLen xs `mod` 2 == 0 = doubleEveryOtherEven xs -- also handles empty list case
| otherwise = doubleEveryOtherOdd xs
この計算では、各数字ペアのどの数字を 2 倍にするかを決定するために、リストに含まれる要素の数が偶数か奇数かを事前に知っておく必要があります。ただし、基本的な Haskell パターン マッチングでは、左から右 (例: x:xs) のリスト要素の一致のみが許可されます。つまり、最後に到達するまで、要素の数が奇数か偶数かを判断できません。リストの最後に到達するためにリストを処理しながら、左側の要素の各ペアで計算を行う必要があるため、それでは遅すぎます。
解決策は、倍増ロジックを 2 つの関数に分割することです。1 つは偶数長のリストを処理し、もう 1 つは奇数長のリストを処理します。3 番目の関数は、指定されたリストに対してこれら 2 つの関数のどちらを呼び出すかを決定するために必要です。リストには、奇数または偶数の要素があるかどうかを確認できるように、リストの長さを計算できる追加の関数が必要です (繰り返しますが、コースのこの段階では長さライブラリ関数は導入されていないためです)。
このソリューションは、第 1 週のレッスンで次のように述べられているアドバイスにも沿っています。