如何在Haskell中像Matlab一样添加两个列表?

3

我想知道是否有可能编写一个Haskell加法运算符,它可以用于两个数字、两个数字列表、两个数字矩阵等等。也就是说,是否有可能定义一个<+>,使得以下所有内容都能正常工作:

1 <+> 2 = 3
[1] <+> [2] = [3]
[[1]] <+> [[2]] = [[3]]
...

我知道在Haskell中添加列表的惯用方法是使用+来处理a,使用zipWith (+)来处理[a]。也许对于[[a]]可以类似地使用zipWith (zipWith (+))等方法。
但是,可以使用类型类或Haskell的其他特性通过一个运算符实现这个功能吗?我知道这只是语法糖,但如果可能的话,它会更加优雅。
-- 更新 --
如@DanielWagner的答案所述,使用(Num a)可能存在问题,最好为基本情况分别定义Integer、Double等。 只是为了记录,我刚刚尝试了建议的方法:
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
class Additive a where (<+>) :: a -> a -> a
instance Num a => Additive a where (<+>) = (+)
instance Additive a => Additive [a] where (<+>) = zipWith (<+>)

或者
{-# LANGUAGE DefaultSignatures #-}
class Additive a where
    (<+>) :: a -> a -> a
    default (<+>) :: Num a => a -> a -> a
    (<+>) = (+)

无论哪种情况,当加载.hs文件或评估[[1,2]] <+> [[3,4]]时都会出现错误。

5
你可以写一个类似于Monoid的类,并创建不同的实例。 - dfeuer
3
要让这个问题更有趣,你需要提供更详细的例子。你想要 [1,2] <+> [3,4] 得到什么结果?[[1,2],[3,4]] <+> [[5,6],[7,8]] 呢?还有 [1,2,3,4] <+> []?需要考虑很多特殊情况。 - Rein Henrichs
1
Matlab/Octave在最后一个例子中会出现维度不匹配的错误,否则它是X(i,j) + Y(i,j) - yǝsʞǝla
1
即使可能,这也是一个不好的想法。如果你问我,Matlab实际上已经很好地证明了这一点... - leftaroundabout
1
@leftaroundabout 我认为这对于Matlab来说是一个糟糕的主意,因为维度被任意地折叠在一起,例如 1 + [2]。 这里不是这种情况吗? - thor
显示剩余4条评论
1个回答

5

是的,这是可能的:

class Additive a where (<+>) :: a -> a -> a
instance Additive Integer where (<+>) = (+)
instance Additive a => Additive [a] where (<+>) = zipWith (<+>)

您在ghci中的三个测试用例:

*Main> 1 <+> 2
3
*Main> [1] <+> [2]
[3]
*Main> [[1]] <+> [[2]]
[[3]]

如果你想要很多实例满足 (<+>) = (+),你可以使用DefaultSignatures来将其作为一个默认的实现:

{-# LANGUAGE DefaultSignatures #-}
class Additive a where
    (<+>) :: a -> a -> a
    default (<+>) :: Num a => a -> a -> a
    (<+>) = (+)

优点是某些实例可以非常短,例如。
instance Additive Integer
instance Additive Int
instance Additive Double
instance Additive Float

不需要额外的方法定义,所有内容都可以按预期工作。


@ReinHenrichs 这只在启用了 FlexibleInstances 的情况下才有效,对吧? - yǝsʞǝla
@AlekseyIzmailov 是的,但 FlexibleInstances 明显是 Haskell 的常见 GHC 方言的一部分。 - Rein Henrichs
@AlekseyIzmailov 实际上你也需要使用 UndecidableInstances,但据我所知这是安全的用法。这只是让编译器知道 AdditiveNum 相同大小是可以接受的。 - Rein Henrichs
@ReinHenrichs 那个实例不会与 [] 重叠吗? - chi
1
@tinlyx 通常情况下,你不能使用 Num a 作为基本情况来使其工作。相反,为你关心的每种类型创建 Additive 的实例 -- IntDouble 或其他类型。(考虑一下如果我写了 instance Num a => Num [a] where ... 会发生什么!) - Daniel Wagner
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接