2

The Haskell adventure continues.

At the moment, i am trying to construct my own Fraction type in Haskell that suits my needs. It's basically just a type Fraction{ n:: Int , d:: Int} and a lot of functions , most of them are of type x->Fraction

So what i wanna do now is making sure that fractions can only be positive, or negative in the numerator, positive in denominator, by converting any other possibilities to one of those.

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

every time some function x->Fraction would return one of those. I assume there must be a smarter way to do this than modifying every x->Fraction function one by one. My guess would be already at the Fraction Type definition.

I haven't been programming for that long so i might not be completely up to beat on the technical terms.

Thanks for reading, and thanks if you answer

EDIT

I decided to go with the Eq Instance solution, and just added that if Signum numerator*Signum denominator doesn't match , return false, to the guard. I had to make that anyways to divide num. and den. with HCD before comparing

The smart-constructor that i originally asked for, is also gonna be used in my Matrix nXn (Q) Module that I'm making on the same time :)

Great answers. Thanks.

4

4 回答 4

8

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

于 2012-05-09T12:31:00.097 回答
5

我实际上会让这些值保持原样并继续计算,但如果你真的想要:将定义Fraction放在一个单独的模块中,不要导出它的构造函数。相反,导出一个类似的函数makeFraction :: Int -> Int -> Fraction,它负责你的“转换”。

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

于 2012-05-09T12:26:20.590 回答
3

我想一个想法是保持内部表示不变。也就是说,在您对数字进行运算之前,您不会关心分子和分母的实际符号。

例如,如果您想比较两个分数数,这将通过派生 Eq 类的实例来完成(这当然需要对类型类有一点了解),您可以简单地检查一下

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

除了检查值。

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

Fraction 6 2 == Fraction 3 1

另一种方法是添加一个单独的符号字段并使用自然数之类的东西作为分子和分母。

于 2012-05-09T12:31:08.333 回答
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
于 2012-05-09T14:20:25.003 回答