1

同じサイズ、同じ内容、同じチェックサムなど、さまざまな「理由」で同じファイルをグループ化する小さな「hello world」タイプのプログラムを作成しています。

したがって、次のような関数を書きたいと思うようになりました (DuplicateReason は、2 つのファイルが同一である理由を示す代数型です)。

getDuplicatesByMethods :: (Eq a) => [((FilePath -> a), DuplicateReason)] -> IO [DuplicateGroup]

各タプルのどこで、最初の関数は、ファイルのパスによってバイト文字列 (コンテンツを含む)、またはチェックサムを含む Word32、サイズを含む Int などの (Eq a) 値を返す関数です。

明らかに、Haskell はこれらの関数が異なる型であることを好まないので、どうにかしてそれらを集める必要があります。

次のようなタイプを作成する唯一の方法

data GroupableValue = GroupString String | GroupInt Int | GroupWord32 Word32

そして、生活を楽にするために、型クラスを次のようにします

class GroupableValueClass a where
  toGroupableValue :: a -> GroupableValue
  fromGroupableValue :: GroupableValue -> a

取得する各値のインスタンスを実装します。

質問: 私はそれを正しく行っていますか? (そうでない場合) このタスクを解決する簡単な方法はありますか?

アップデート:

これは、私が望むものを説明する必要がある完全な最小限のコードです(簡素化され、IOなどはありません):

data DuplicateGroup = DuplicateGroup

-- method for "same size" -- returns size
m1 :: String -> Int
m1 content = 10

-- method for "same content" -- returns content
m2 :: String -> String
m2 content = "sample content"

groupByMethods :: (Eq a) => [(String -> a)] -> [DuplicateGroup]
groupByMethods predicates = undefined

main :: IO ()
main = do
  let groups = (groupByMethods [m1, m2])
  return ()
4

1 に答える 1

2

aリストは常に同種であるため、同じリストに別のアイテムを入れることはできません(お気付きのように)。これを回避する設計方法はいくつかありますが、私は通常GADTを使用することを好みます。例えば:

{-# LANGUAGE GADTs #-}

import Data.ByteString (ByteString)
import Data.Word

data DuplicateReason = Size | Checksum | Content
data DuplicateGroup

data DuplicateTest where
    DuplicateTest :: Eq a => (FilePath -> IO a) -> DuplicateReason -> DuplicateTest

getSize :: FilePath -> IO Integer
getSize = undefined

getChecksum :: FilePath -> IO Word32
getChecksum = undefined

getContent :: FilePath -> IO ByteString
getContent = undefined

getDuplicatesByMethods :: [DuplicateTest] -> IO [DuplicateGroup]
getDuplicatesByMethods = undefined

このソリューションにはまだ新しい型が必要ですが、少なくとも事前にすべてのケースを指定したり、ボイラープレート型クラスを作成したりする必要はありません。現在、ジェネリック型aは本質的に GADT 内に「隠されている」ため、GADT にラップされた、さまざまな戻り値の型を持つ関数を含むリストを定義できますDuplicateTest

getDuplicatesByMethods
    [ DuplicateTest getSize Size
    , DuplicateTest getChecksum Checksum
    , DuplicateTest getContent Content
    ]

また、関数を再考するだけで、言語拡張を使用したり、新しい型を導入したりすることなく、これを解決できます。主な意図は、いくつかのプロパティに従ってファイルをグループ化することです。したがって、次のようaに定義できます。getDuplicatesByMethods

getDuplicatesByMethods :: [([FilePath] -> IO [[FilePath]], DuplicateReason)] -> IO [DuplicateGroup]

つまり、いくつかの基準に従ってファイルをグループ化する関数を取り入れます。次に、ヘルパー関数を定義できます

groupWith :: Eq a => (FilePath -> IO a) -> [FilePath] -> IO [[FilePath]]

getDuplicatesByMethodsそして、このように呼び出します

getDuplicatesByMethods
    [ (groupWith getSize, Size)
    , (groupWith getChecksum, Checksum)
    , (groupWith getContent, Content)
    ]
于 2013-01-04T08:24:53.610 に答える