输入某种类型的“母功能”

时间:2012-05-09 12:11:26

标签: haskell

Haskell冒险继续。

目前,我正在尝试在Haskell中构建我自己的Fraction类型,以满足我的需求。 它基本上只是一个类型Fraction {n :: Int,d :: Int}和许多函数,其中大多数都是x->分数

所以我现在要做的就是确保分数只能是正数,或者分子中是负数,分母中是正数,通过将任何其他可能性转换为其中一种。

   --e.g
      (Fraction (3 (-5))->Fraction (-3) 5
   --and
      (Fraction (-3) (-5))->Fraction 3 5

每次某些函数x-> Fraction都会返回其中一个。 我认为必须有一种更聪明的方法来做到这一点,而不是逐个修改每个x->分数函数。我的猜测已经在分数类型定义中了。

我没有那么长时间的编程,所以我可能不会完全胜过技术术语。

感谢阅读,谢谢你的回答

修改

我决定使用Eq Instance解决方案,并且添加了如果Signum分子* Signum分母不匹配,则返回false给警卫。无论如何我不得不分数。和书房。在比较之前用HCD

我最初要求的智能构造函数也将用于我同时制作的Matrix nXn(Q)模块中:)

很棒的答案。感谢。

4 个答案:

答案 0 :(得分:8)

您可以使用smart constructors来确保对类型的这些约束,并通过不导出它们来隐藏数据构造函数。智能构造函数本质上是一种函数,可以保证类型系统无法实现的约束。

答案 1 :(得分:5)

我实际上会让它们像它们一样继续计算,但如果你真的想要:将Fraction的定义放在一个单独的模块中,不要导出它的构造函数。而是导出像makeFraction :: Int -> Int -> Fraction这样的函数来处理“转换”。

现在,模块外的每个人都只能按照你想要的方式构建分数。

答案 2 :(得分:3)

我认为一个想法就是保持内部表现形式。也就是说,在你对你的数字进行操作之前,你不会关心你的分子和分母的实际符号。

例如,如果你想比较两个分数,这可以通过派生Eq类的实例来完成(当然这涉及到对type classes有一点了解),你可以简单地检查一下

signum d1 * signum n1 == signum d2 * signum n2

除了检查值。

请注意,在处理分数时还需要检查其他方面。例如:

Fraction 6 2 == Fraction 3 1

替代方案将涉及添加单独的符号字段并使用Natural数字作为分子和分母。

答案 3 :(得分:3)

如果你真的使用的是Int而不是Integer,那么类型系统(和实现)可以解决这个特殊问题而不需要任何技巧或隐藏。类型Word表示正整数机器:

Prelude Data.Word> [minBound,maxBound] :: [Word]
[0,18446744073709551615]
Prelude Data.Word> [minBound,maxBound] :: [Int]
[-9223372036854775808,9223372036854775807]

所以你可以写:

import Data.Word

data Fraction = Int :/ Word deriving Show

fi = fromIntegral

instance Eq Fraction where
  (i :/ w) == (j :/ v) = i * fi v == j * fi w

instance Num Fraction where
  fromInteger n = fromInteger n :/ 1
  (i :/ w) * (j :/ v) = (i * j) :/ (w * v)
  (i :/ w) + (j :/ v) = i * fi v + j * fi w :/ w * v 
  (i :/ w) - (j :/ v) =  (i :/ w) + (negate j :/ v)
  negate (i :/ w) = (negate i:/ w)
  abs (i :/ w) = (abs i :/ w)  
  signum (i :/ w) = (signum i :/ 1)

您将通过严格和解包获得更多类似于机器号的行为:

 data Fraction = {-#UNPACK#-} !Int :/ {-#UNPACK#-} !Word deriving Show