ハスケルの組合せ
23809 ワード
ジオンムンジンジン
許可Open Software License 3.0
そして、私はHaskellで組合せを作る方法に夢中です.
Combinations In Haskell (called Tail After Tail) テールストーリー後のテール
プログラミング世界では、擬似コードや理論は、多くの方法でプログラミングの実装をトリガします.この種の転換は、同様にプログラミングをおもしろくします.
以前私はTailAfterTail.lhs
私は
ごく少量のコードで構成されているが、非常に高い周波数で動作するいくつかの異なった実装が、実行の多くの反復の後にパフォーマンスの大きなギャップを最終的に示すでしょう.
したがって、これは尾の組み合わせの後に尾の実装中に見つけたものです.
まず、すべての機能を書き留めるつもりです.
詳細情報をご覧くださいHERE .
しかし
scanlまたはzipbyなしで次のコードが作成されます.
それはわずかにより多くのパフォーマンスを得ます.
これは別の記事にあるかもしれません.
しかし、IMHOは、それは怠惰を減らすために、より少ないスタックを使用するのに役立ちます.
私はその関数を
これは別のバージョンなし
この関数はジョブを2つの操作に分離します: 前の尾から最初のケースを作成します. ケースの残りを作成し、結果に基づいて次の次のケースを開始します.
現在
今すぐ覚えなさい.時間を節約する
イン
ところで、
簡単に
次のステップは利用できません.私はそれをunsafechenと言う必要はありません.
今、それを選択する時間です
そして、これは一般的なヘルパー関数であることがわかりました.
あなたはベンチマークコードを見つけることができますmy github repository .
あなたの時間を節約する.THIS
私のベンチマーク結果の1つです.
ベンチマーキング後
それらの中のすべてのカテゴリ(小、中、大)で.
許可Open Software License 3.0
私の好きな言語の間に戦争があった。
そして、私はHaskellで組合せを作る方法に夢中です.
Haskellシリーズの組合せ
同じ説異なる実装
プログラミング世界では、擬似コードや理論は、多くの方法でプログラミングの実装をトリガします.この種の転換は、同様にプログラミングをおもしろくします.
以前私はTailAfterTail.lhs
私は
inits
or tails
私が頼らないならば、全く必要でありませんscanl
or zipWith
.ごく少量のコードで構成されているが、非常に高い周波数で動作するいくつかの異なった実装が、実行の多くの反復の後にパフォーマンスの大きなギャップを最終的に示すでしょう.
したがって、これは尾の組み合わせの後に尾の実装中に見つけたものです.
モジュール開始
まず、すべての機能を書き留めるつもりです.
{-# LANGUAGE BangPatterns #-}
module TailAfterTail
( combinationsWithScanl
, combinationsWithSingleStep
, combinations
, combinationsWithTwoSteps
, allCombinationsWithScanl
, allCombinationsWithSingleStep
, allCombinationsWithTwoSteps
, allCombinations
) where
import Data.List (tails, inits, scanl') -- only required for **WithScanl
オリジナルバージョン
詳細情報をご覧くださいHERE .
しかし
combinations1'
, flatten_allCombinationsGrouped
andgenPart
共通のヘルパー関数になります.combinations1' :: [a] -> [[[a]]]
combinations1' ms = [ [[m]] | m <- ms ]
flatten_allCombinationsGrouped allComboFunc = map concat . allComboFunc
genPart :: Foldable t => a -> t [[a]] -> [[a]]
genPart leader followerGroups = [ leader : followers
| followers <- concat followerGroups ]
そしていくつかのヘルパー関数を定義します.usefulTails :: [a] -> [[a]]
usefulTails = init . tails
genStep :: [[[a]]] -> [a] -> [[[a]]]
genStep prevTails members' =
zipWith genPart members' (usefulTails prevTails')
where
prevTails' = tail prevTails -- head is not useful
membersTails = reverse . tail . inits -- tail is used to skip empty list.
そしてついにcombinationsWithScanl
家族は下に行きます.allCombinationsWithScanl' :: [a] -> [[[[a]]]]
allCombinationsWithScanl' ms =
scanl' genStep (combinations1' ms) (membersTails ms)
allCombinationsWithScanlGrouped :: [a] -> [[[a]]]
allCombinationsWithScanlGrouped =
flatten_allCombinationsGrouped allCombinationsWithScanl'
allCombinationsWithScanl :: [a] -> [[a]]
allCombinationsWithScanl = concat . allCombinationsWithScanlGrouped
SANLを使用しない純粋な実装( singlestep )
scanlまたはzipbyなしで次のコードが作成されます.
それはわずかにより多くのパフォーマンスを得ます.
これは別の記事にあるかもしれません.
しかし、IMHOは、それは怠惰を減らすために、より少ないスタックを使用するのに役立ちます.
unsafe_allCombinationsWithSingleStep :: [a] -> [[[[a]]]]
unsafe_allCombinationsWithSingleStep members =
let
helper ! cases = -- bang pattern added
let
genStep (m:ms) (_:cs:[]) = [ [ m : c | c <- cs ] ]
genStep (m:ms) (_:cs) =
-- note ^ : we don't use first element
genPart m cs : genStep ms cs
in
cases : helper (genStep members cases)
in
helper . combinations1' $ members
ご覧の通りhelper
関数はエントリレベルのラッパー関数であり、再帰呼び出しを行う.genStep
実際に次のケースを作成し、Haskellのlazinessのおかげで後で評価されるthunkとして動作します.私はその関数を
unsafe_
故意に.ヘルパー機能は実際には停止するときに知っているので、実行している場合unsafe_allCombinationsWithSingleStep
裸の文脈では、例外で爆発します.sh> ghci
GHCi, version 8.10.7: https://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /your/home/.config/ghc/ghci.conf
λ> :l 2022-04-15-Combinations-TailAfterTail'.lhs
[1 of 1] Compiling TailAfterTail ( 2022-04-15-Combinations-TailAfterTail'.lhs, interpreted )
Ok, one module loaded.
λ> unsafe_allCombinationsWithSingleStep [1..5]
[[[[1]],[[2]],[[3]],[[4]],[[5]]],[[[1,2],[1,3],[1,4],[1,5]],[[2,3],[2,4],[2,5]],[[3,4],[3,5]],[[4,5]]],[[[1,2,3],[1,2,4],[1,2,5],[1,3,4],[1,3,5],[1,4,5]],[[2,3,4],[2,3,5],[2,4,5]],[[3,4,5]]],[[[1,2,3,4],[1,2,3,5],[1,2,4,5],[1,3,4,5]],[[2,3,4,5]]],[[[1,2,3,4,5]]],[[]*** Exception: 2022-04-15-Combinations-TailAfterTail'.lhs:(120,9)-(122,52): Non-exhaustive patterns in function genStep
unsafe_allCombinationsWithSingleStepGrouped
結果を平らにするが、選択サイズでグループ化したが、これはまだ安全ではない combinationsWith
は処理します.unsafe_allCombinationsWithSingleStepGrouped :: [a] -> [[[a]]]
unsafe_allCombinationsWithSingleStepGrouped =
flatten_allCombinationsGrouped unsafe_allCombinationsWithSingleStep
それで、我々はより多くの時間を平らにすることによって、すべての組合せを得ることができました.allCombinationsWithSingleStep :: [a] -> [[a]]
allCombinationsWithSingleStep members =
concat
-- this makes unsafe_* safe by limiting the size of list.
. take (length members)
. unsafe_allCombinationsWithSingleStepGrouped
$ members
2歩で
これは別のバージョンなし
scanl
. 主な改善はこの関数はジョブを2つの操作に分離します:
allCombinationsWithTwoSteps' :: [a] -> [[[[a]]]]
allCombinationsWithTwoSteps'
members@(fm:rms) = -- ^ fm : first member; rms: rest members
let
initFirstCase = [[fm]]
initRestCases = combinations1' rms
genFirstCases = genPart fm
genRestCases _ [] = []
genRestCases (m:ms) rcs@(_:rcs') = -- ^ rcs : rest of cases
(genPart m $ rcs) : (genRestCases ms rcs')
比較するとほぼ同じに見えるSingleStep
でも現在
helper
関数は、どこから始まるべきかを正確に知るnewTail
is今すぐ覚えなさい.時間を節約する
tail
パターンマッチングイン
SingleStep
しかし、選択肢が成長しているとき、Ressutsは伝播されます.ところで、
tail
以下のコードで、パターンマッチング手段(Con : CS)によって. genStep (m:ms) (_:cs) =
-- note ^ : we don't use first element
[ m : c | c <- concat cs ] : genStep ms cs
そして、最初のケースとヘルパー機能でそれらを配線しましょう helper [] = []
helper ! prevTail =
let
newTail = genRestCases rms (tail prevTail)
in
((genFirstCases prevTail) : newTail) : helper newTail
in (initFirstCase : initRestCases) : helper initRestCases
以下の手順は他の実装と同様です.allCombinationsWithTwoStepsGrouped :: [a] -> [[[a]]]
allCombinationsWithTwoStepsGrouped =
flatten_allCombinationsGrouped allCombinationsWithTwoSteps'
allCombinationsWithTwoSteps :: [a] -> [[a]]
allCombinationsWithTwoSteps members =
concat . allCombinationsWithTwoStepsGrouped $ members
もう一つの利点TwoSteps
実行は、我々が止めることができるということです簡単に
newTail
は常に利用可能です.次のステップは利用できません.私はそれをunsafechenと言う必要はありません.
各実装からの
今、それを選択する時間です
K
与えられた選択から.そして、これは一般的なヘルパー関数であることがわかりました.
組み合わせ
combinationsWith :: ([a] -> [[[a]]]) -> [a] -> Int -> Int -> [[a]]
combinationsWith allComboGroupedFunc ms n1@selectFrom n2@selectTo =
let
( isFlipped, n1', n2' ) = -- smaller value first
if n1 < n2 then ( False
, max n1 0
, max n2 0)
else ( True
, max n2 0
, max n1 0)
-- and ensure all range value are zero or positive by usig `max`
rangeLength = n2' - n1' + 1
reverseIfNeeded
| isFlipped = reverse
| otherwise = id
in
-- note: read from the bottom
concat -- 4. final flattening
. reverseIfNeeded -- 3. if user put opposite way, reverse it.
. take rangeLength -- 2. takes only interested lists
. drop (pred n1') -- 1. ignore some
$ allComboGroupedFunc ms
すべてのvariantの組み合わせは以下のようになります.combinationsWithScanl = combinationsWith allCombinationsWithScanlGrouped
combinationsWithSingleStep = combinationsWith unsafe_allCombinationsWithSingleStepGrouped
combinationsWithTwoSteps = combinationsWith allCombinationsWithTwoStepsGrouped
ベンチマーク
あなたはベンチマークコードを見つけることができますmy github repository .
あなたの時間を節約する.THIS
私のベンチマーク結果の1つです.
デフォルトの組み合わせと組み合わせを選択
ベンチマーキング後
AllcombinationsWithTwoSteps
最良結果それらの中のすべてのカテゴリ(小、中、大)で.
allCombinations = allCombinationsWithTwoSteps
combinations = combinationsWithTwoSteps
Reference
この問題について(ハスケルの組合せ), 我々は、より多くの情報をここで見つけました https://dev.to/jeongoon/combinations-in-haskell-10egテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol