【问题标题】:How to overload a function for multiplying [Double] in Haskell (ad-hoc polymorphism)?如何在 Haskell 中重载用于乘法 [Double] 的函数(临时多态性)?
【发布时间】:2015-10-17 09:54:47
【问题描述】:

在 Haskell 中实现即席多态(函数重载)的方法是通过类型类(请参阅thisthisthis 问题的答案等)。

但我正在努力为以下情况定义重载的mult(产品)函数:

mult: [Double] -> Double -> [Double]
mult: Double -> [Double] -> [Double]
mult: [Double] -> [Double] -> [Double]

谢谢

(至少需要 case 1 [Double]*Double 和 case 3 [Double]*[Double])。

【问题讨论】:

  • 你为什么要这个?只是因为Matlab allows multiplying anything you throw at it doesn't mean this is a good idea。查看vector-space 以正确处理多维乘法。或者,如果你不太关心数学的优雅,你可以使用hmatrix(实际上很像 Haskell 中的 Matlab/Octave)或linear
  • @leftaroundabout 感谢您的链接。你的意思是这种多态性不是一个好主意(不仅matlab允许)? “一般”或在 Haskell 中这是一个坏主意吗?
  • 嗯,我认为这通常是个坏主意,在 Haskell 中真的没有必要,因为你可以写 map (*x) yszipWith (*) xs ys 来明确你的意图。这当然不适用于应该同时处理标量和向量的多态代码——但是,编写这样的代码来处理任何长度的标量或列表是自找麻烦。指定哪个列表需要与哪个其他列表匹配的长度以及结果的长度等是很尴尬的。这就是vector-spacelinear 的亮点,因为它们在编译时检查尺寸。
  • @leftaroundabout 感谢您的帮助。

标签: haskell overloading


【解决方案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 的哪个重载:12 是多态的,可以是任何数字类型,而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]

【讨论】:

  • 我认为你错了,我希望有人更正“我的代码”。我只想要这个问题的一般解决方案(如果有的话)。忘记代码。我的笔记只是为了表明我不只是在等待任何人完成这项工作。感谢您的回答。试试看。
【解决方案2】:

我已经设法做到了。当然不是很好。

我认为任何人都应该考虑 leftaroundao 的 cmets 和批评者,但为了方便和相关,我在下面引用了这个问题。

{-# 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) yszipWith (*) xs ys 让你的意图明确。这当然不适用于 应该同时处理标量和向量的多态代码—— 然而,编写这样的代码只是为了处理任何的标量或列表 长度是自找麻烦。很难指定哪个 列表需要有一个长度匹配哪个其他列表和什么长度 结果将是等等。这是向量空间或线性发光的地方, 因为他们在编译时检查尺寸。

【讨论】:

    猜你喜欢
    • 2023-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多