Haskell编写在其中工作的构造函数



我有几个问题

我写了一个叫做rope的构造函数,就像这样

  data Rope = TextRope{ropeText :: String}
            | ConcatRope{rope1 :: Rope, rope2 :: Rope}
            | SubRope{subRopetext :: Rope, starting :: Integer, ending :: Integer}
            deriving Show

首先,当我像这样创建TextRope

*Main> let s =TextRope "why"
*Main> s
TextRope {ropeText = "why"}
*Main> 

当我执行s时,我只想获得构造函数的字符串,这就是为什么我不太确定。

对concat和sub构造函数也很好奇。具体来说,在我看来你调用了这两个构造函数确实发生了一些事情,你返回了将rope 1和rope 2连接在一起的结果,我不知道在这个语言中该怎么描述,你定义了数据结构但它的返回结果必须由结构

计算出来

下面是一些关于这些函数如何工作的例子

> let tr = TextRope "Hello,"
> let sr = TextRope " world!"
> let hw = ConcatRope tr sr
> let ow = SubRope hw 4 4
> tr
Hello,
> sr
 world!
> hw
Hello, world!

总的来说有点混乱,对于haskell构造函数和数据类型来说是新的,所以一些指针会有所帮助(虽然不是c指针!)

数据构造函数从来不工作。它们只保存你传递给它们的数据。如果想要完成工作,应该定义所谓的智能构造函数,它们只是在将操作传递给实际构造函数之前执行一些操作的函数。例如

data Fraction = Fraction
    { numerator :: Int
    , denominator :: Int
    } deriving (Eq)
(%) :: Int -> Int -> Fraction
x % y =
    let (a, b) = reduce x y
    in Fraction a b
-- Reduces a fraction to it's simplest terms
reduce :: Int -> Int -> (Int, Int)
reduce num den = undefined

这里你不会从你的模块中导出Fraction构造函数,只导出%函数,它以最简化的形式构造一个。

您遇到的另一个问题是您希望构造函数以不同的方式输出。你可以通过不推导Show来达到这个目的。但是,我要警告的是,Haskell约定是show . read = read . show = id,它不适合您想要做的事情。不过,这并不是一个严格的约定,没有什么可以阻止您这样做:

data Rope = <your implementation minus the deriving Show bit>
instance Show Rope where
    show (TextRope t) = t
    show (ConcatRope r1 r2) = show r1 ++ show r2
    show (SubRope r starting ending) = <exercise left to reader>

作为题外话,我建议不要使用具有不同字段名称的sum类型的记录,这可能会导致您的程序类型检查但包含错误的问题,如果编写不同,则可以在编译时捕获这些错误。例如,如果代码
> ropeText (ConcatRope (TextRope "Hello, ") (TextRope "world!"))

这会导致错误并使程序崩溃!相反,看起来您只需要一个具有concatsubRope函数的Rope类型,因此您可以非常简单地将其实现为

data Rope = Rope String deriving (Eq)
concatRope :: Rope -> Rope -> Rope
concatRope (Rope r1) (Rope r2) = Rope (r1 ++ r2)
-- Why use Integer instead of Int?  You might find it's easier to implement this function
subRope :: Rope -> Integer -> Integer -> Rope
subRope (Rope r) start end = Rope $ substr start end r
    where substr s e text = <exercise left to reader>

现在绝对没有办法进行非法的rope操作,唯一的区别是现在你必须使用concatRope代替ConcatRopesubRope代替SubRope。你可以保证这些函数会做你想做的事情,你不会有一些复杂的类型,无论如何都不能帮助你。

如果你不实现你自己的节目(不使用自动派生),你将很难得到你想要的。

但是如果你这样做,这很简单:

data Rope = TextRope{ropeText :: String}
            | ConcatRope{rope1 :: Rope, rope2 :: Rope}
            | SubRope{subRopetext :: Rope, starting :: Integer, ending :: Integer}
instance Show Rope where
    show (TextRope s) = s
    show (ConcatRope a b) = show a ++ show b

我相信你会找到SubRope情况下自己的实现;)

您的示例代码和示例交互结果不匹配。这就是您如何定义Rope:

data Rope = TextRope{ropeText :: String}
          | ConcatRope{rope1 :: Rope, rope2 :: Rope}
          | SubRope{subRopetext :: Rope, starting :: Integer, ending :: Integer}
          deriving Show

deriving Show部分是关键;我们将看到如何。

之后显示这个示例输出:

> let tr = TextRope "Hello,"
> let sr = TextRope " world!"
> let hw = ConcatRope tr sr
> hw
Hello, world!

对于我刚才展示的代码,实际上,您将看到如下内容:

> hw
ConcatRope { rope1 = TextRope "Hello,", rope2 = TextRope " world!" }

我们可以得到你所描述的输出的唯一方法是,如果我们在你的Rope定义中去掉deriving Show子句,并这样写:

instance Show Rope where
    show (TextRope text) = text
    show (ConcatRope r1 r2) = show r1 ++ show r2
    show (SubRope rope start end) = ...

最新更新