我有一些数据,我想测试一下,存储它们的最佳方法是什么.我正在决定二进制,四进制,八进制和十六进制数据类型.
检查哪个是最好的唯一方法是在我的算法中使用它们,然后查看结果.
到目前为止我的实现看起来像这样:
{-# LANGUAGE DeriveAnyClass #-}
data Binary = O | I
deriving (Bounded, Enum, Eq, Ord, Show)
data Octal = OA | OB | OC | OD | OE | OF | OG | OH
deriving ( ... )
Run Code Online (Sandbox Code Playgroud)
但是我想要更多的通用构造函数data Binary = 0 | 1.有可能吗?
获取所需的确切语法是不可能的,但有一些方法可以通过共享构造函数的方式一般定义"0和n"类型,然后使用数字文字作为抽象构造函数.
的有限typelits包提供参数化在其上了,所以元件的数量的类型:
x :: Finite 4
Run Code Online (Sandbox Code Playgroud)
将是一个有四个居民的类型:0,1,2和3.如果你有一个类型的值Finite 4,你可以确定它是这四个值之一(或某些愚蠢的undefined或其他一些底部).您不能直接对其进行模式匹配,但您可以使用将项目投影Finite 4到整数中getFinite :: Finite n -> Integer.
所以在这个方案中,你有:
type Binary = Finite 2
type Quad = Finite 4
type Octal = Finite 8
Run Code Online (Sandbox Code Playgroud)
你可以间接模式匹配:
processBinary :: Binary -> String
processBinary d = case getFinite d of
0 -> "It's a zero"
1 -> "it's a one"
_ -> -- this case should be impossible, even though the compiler can't verify that.
Run Code Online (Sandbox Code Playgroud)
而且,您甚至可以使用其(部分)Num实例使用数字文字"构造"它们:
> putStrLn (processBinary 0)
It's a zero
> putStrLn (processBinary 0)
It's a one
Run Code Online (Sandbox Code Playgroud)
所以这可能是你想要的最接近的东西!但是有一些缺点 - 编译器无法验证您的模式匹配语句是否完整,并且它也无法验证您的数字文字实际上是否有效且不会超出您的类型的范围重新使用.
> putStrLn (processBinary 2)
** Error: Runtime error! Sucks :'(
Run Code Online (Sandbox Code Playgroud)
还有方便weakenN和strengthenN功能,它允许你使用a Finite 2就像它是一个Finite 8(使用二进制数字,就好像它是一个八进制数字),并使用a Finite 8就好像它是一个Maybe (Finite 2).
binDigitToOctalDigit :: Binary -> Octal -- Finite 2 -> Finite 8
binDigitToOctalDigit = weakenN
Run Code Online (Sandbox Code Playgroud)
还有另一种方法与你想要的完全不符,但有一些优点.您可以做的是具有递归/归纳定义的类型(如列表),这些类型被"构造"为仅具有如此多的构造函数,在某些类型级数字上再次参数化. 类型组合器提供一种这样的类型.那里,你有:
type Binary = Fin (S (S Z)) -- "2"
type Octal = Fin (S (S (S (S (S (S (S (S Z)))))))) -- "8"
Run Code Online (Sandbox Code Playgroud)
该库确实提供了方便的类型同义词,因此您可以改为:
type Binary = Fin N2
type Quad = Fin N4
type Octal = Fin N8
Run Code Online (Sandbox Code Playgroud)
并且Fin类型被构造为具有由类型指示的构造函数的数量. Fin N2有两个构造函数:
FZ :: Fin N2FS FZ :: Fin N2并Fin N8有八个构造函数,FZ,FS FZ,FS (FS FZ),等.
Fin也有一个投影功能fin :: Fin n -> Int,所以你可以像使用它一样使用它Finite,并在Int文字上使用模式匹配. 但! 这样做的好处是你也可以在构造函数上直接模式匹配,编译器将确保完整性:
processBinary :: Fin N2 -> String
processBinary d = case d of
FZ -> "It's a zero"
FS FZ -> "It's a one"
Run Code Online (Sandbox Code Playgroud)
GHC实际上可以验证你已经处理了每个选项:)
并且,您也不会意外地使用"超出范围"的构造函数:
> putStrLn (processBinary FZ)
It's a zero
> putStrLn (processBinary (FS (FS FZ))
-- that is a compile error! neat, the compiler would tell you that this isn't allowed!
Run Code Online (Sandbox Code Playgroud)
该库定义weaken类似于有限类型库的工作方式.它没有定义strengthen,但如果你需要它可以实现它:)
binDigitToQuadDigit :: Binary -> Quad -- Fin N2 -> Fin N4
binDigitToQuadDigit = weaken . weaken
Run Code Online (Sandbox Code Playgroud)