4

简短形式(这将至少以一种方式解决我的问题)

我该怎么做这样的事情:

try_to_show :: a -> String
try_to_show val = if (val is instance of Show) (show val) else "Cannot show"

我可能完全错误地这样做了(unhaskell 方式);我只是在学习,所以请让我知道是否有更好的方法来解决这个问题。

上下文:我正在写一堆树结构。我想将我的prettyprint函数重用于二叉树。并非所有的树都可以使用泛型Node/Branch数据类型;不同的树需要不同的额外数据。因此,为了重用prettyprint我想创建一个类的函数,不同的树将是以下实例:

class GenericBinaryTree a where
    is_leaf :: a -> Bool
    left :: a -> a
    node :: a -> b
    right :: a -> a

这样他们只需要实现获取左、右和当前节点值的方法,而prettyprint不需要知道内部结构。

然后我来到这里:

prettyprint_helper :: GenericBinaryTree a => a -> [String]
prettyprint_helper tree
    | is_leaf tree = []
    | otherwise = ("{" ++ (show (node tree)) ++ "}") : (prettyprint_subtree (left tree) (right tree))
        where
            prettyprint_subtree left right =
                ((pad "+- " "|  ") (prettyprint_helper right)) ++ ((pad "`- " "   ") (prettyprint_helper left))
            pad first rest = zipWith (++) (first : repeat rest)

我得到了Ambiguous type variable 'a0' in the constraint: (Show a0) arising from a use of 'show'错误(show (node tree))

这是最基本的树数据类型和实例定义的示例(我的其他树有其他字段但它们与通用prettyprint函数无关)

data Tree a
    = Branch (Tree a) a (Tree a)
    | Leaf
instance GenericBinaryTree (Tree a) where
    is_leaf Leaf = True
    is_leaf _ = False
    left (Branch left node right) = left
    right (Branch left node right) = right
    node (Branch left node right) = node

我本可以node :: a -> [String]在每个实例/类型的树中定义和处理字符串化,但这感觉更整洁。就 而言prettyprint,我只需要一个字符串表示,但如果我稍后添加其他通用二叉树函数,我可能需要实际值。

那么,无论节点值是否为实例,我如何编写它才能工作Show?或者我应该以什么其他方式来解决这个问题?在面向对象的语言中,我可以很容易地检查一个类是否实现了某些东西,或者一个对象是否有一个方法。


我不能使用类似的东西

prettyprint :: Show a => a -> String

因为需要显示的不是树,而是树内部的值(由 function 返回node)需要显示。我还尝试更改nodeShow b => a -> b没有运气(以及一堆其他类型的类/先决条件/无论如何/我什至不知道我在做什么)。

4

2 回答 2

4

你不能做你在问题中提到的第一个解决方案。你可以做的是尝试类似的事情:

class GenericBinaryTree t where
    is_leaf :: t a -> Bool
    left :: t a -> t a
    node :: t a -> a
    right :: t a -> t a

prettyprint_helper :: (GenericBinaryTree f, Show a) => f a -> [String]

在这里,我们使用类型类来指定可以导航树的方式,这解决了树可以具有不同结构的问题。下一个问题是如何显示节点值,这是通过在签名中添加Show类型类约束来解决的prettyprint_helperGenericBinaryTree实例实现:

instance GenericBinaryTree Tree where
  is_leaf Leaf = True
  is_leaf _ = False
  left (Branch left node right) = left
  right (Branch left node right) = right
  node (Branch left node right) = node
于 2013-10-22T05:00:59.637 回答
2

在你的课堂上,你有node :: a -> bwherea被固定为实例化的东西GenericBinaryTree,但b它是......真正的任何东西。完全没有任何限制,您最终会得到一个根本无法使用的值,更不用说show.

该方法提供了一个必须能够node :: Show b => a -> b的约束,但是您会遇到第二个问题:具体来说,我们仍然不知道是什么!bShowb

特别是,这就是为什么在class声明中包含不受约束的类型变量很少是一个好主意的原因。你会在像这样的地方看到它们

instance Alternative f where
  empty :: f a

建议我们有能力创建一个f里面有零元素的容器......所以它也可以采用任何f a类型a

一个解决方案可能是使用 MultiParamTypeClasses

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class GenericBinaryTree b a | a -> b where
  is_leaf :: a -> Bool
  left :: a -> a
  node :: a -> b
  right :: a -> a

showNode :: (Show b, GenericBinaryTree b a) => a -> String
showNode = show . node

编译就好了。

于 2013-10-22T03:49:16.720 回答