在Haskell中使用ad-hoc多态(函数重载)的方法是通过类型类(参见this,this和this问题的答案等)。
但是我很难为以下情况定义一个重载的mult
(产品)函数:
mult: [Double] -> Double -> [Double]
mult: Double -> [Double] -> [Double]
mult: [Double] -> [Double] -> [Double]
由于
(至少,案例1 [Double]*Double
和案例3 [Double]*[Double]
是必要的。)
答案 0 :(得分:1)
与往常一样,我在尝试(没有成功)这个"并不像你想的那样有用:你包含你的代码很好,但如果你从编译器收到错误信息,告诉我们它是什么!他们非常有教育意义,并且出于某种原因打印出来。
我刚试过你写的东西,这实际上是你(可能)得到的错误信息:
*Multiplication> mul 1 [2]
Non type-variable argument
in the constraint: Multipliable ta [t] tc
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
it :: forall ta tc t. (Num ta, Num t, Multipliable ta [t] tc) => tc
现在,您可以尝试启用FlexibleContexts,但这似乎无法解决问题。但是,正如编译器告诉您它在推断类型时遇到问题时通常的情况一样,您应该尝试添加一些显式类型,看看是否有帮助:
*Multiplication> mul (1::Double) [2 :: Double]
[2.0]
基本上,编译器无法确定您想要的mul
重载:1
和2
是多态的,可以是任何数字类型,而且只有mul
现在的一个合适的重载,编译器不进行这样的推断,除非它可以证明在这种情况下不会存在其他重载。完全指定参数类型足以解决问题。
解决此特定问题的另一种方法是为每个参数使用类型类,将其转换为规范类型[Double]
,而不是整个参数的类型类。这是一个比一般ad hoc多态更具体的解决方案,并不是所有问题都适合,但对于像处理数字列表这样的单个数字这样的事情应该没问题:
module Multiplication where
import Control.Monad (liftM2)
class AsDoubles a where
doubles :: a -> [Double]
instance AsDoubles Double where
doubles = return
instance AsDoubles [Double] where
doubles = id
mult :: (AsDoubles a, AsDoubles b) => a -> b -> [Double]
mult x y = liftM2 (*) (doubles x) (doubles y)
*Multiplication> mult [(1 :: Double)..5] [(1 :: Double)..3]
[1.0,2.0,3.0, -- whitespace added for readability
2.0,4.0,6.0,
3.0,6.0,9.0,
4.0,8.0,12.0,
5.0,10.0,15.0]
答案 1 :(得分:1)
我设法这样做了。当然不是很好。
我认为任何人都应该考虑左下方的评论和评论,我为了方便和相关而在下面引用。
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}
class Multipliable ta tb tc | ta tb -> tc where
mul :: ta -> tb -> tc
instance Multipliable [Double] Double [Double] where
mul p k = map (*k) p --mul p k = map (\a -> k * a) p
instance Multipliable Double [Double] [Double] where
mul k p = map (*k) p --mul p k = map (\a -> k * a) p
instance Multipliable [Double] [Double] [Double] where
mul p q = p -- dummy implementation
r = [1.0, 2.0, 3.0] :: [Double]
r1 = (mul :: [Double] -> Double -> [Double]) r 2.0
r2 = (mul :: Double -> [Double] -> [Double]) 2.0 r
r3 = (mul :: [Double] -> [Double] -> [Double]) r1 r2
main = do
print r1
print r2
print r3
为什么你还想要这个呢?仅仅因为Matlab允许乘法 你抛出的任何东西并不意味着这是一个好主意。查看 矢量空间正确处理 多维-乘法。或者,如果你不在乎 很多数学优雅,你可以使用hmatrix(事实上 很像Haskell中的Matlab / Octave),或线性的。
我认为一般来说这是一个坏主意,在Haskell中真的没用,因为你可以写
map (*x) ys
或zipWith (*) xs ys
让你明确意图。这当然不起作用 应该处理标量和向量的多态代码 - 但是,编写这样的代码只是处理标量或任何列表 相反,长度是在寻找麻烦。指定哪个是很尴尬的 list需要有一个长度匹配哪个其他列表和长度 结果将是等等。这是矢量空间或线性光泽, 因为他们在编译时检查尺寸。