我正在使用Haskell并声明Vector
为
data Vector = Vector [Double]
现在,我想将两个向量的dot
乘积声明为
dot :: Vector -> Vector -> Double
dot a b = sum $ a * b -- I already wrote Vector as an instance of Num for *.
但问题是,我收到了错误
Couldn't match expected type [a0] with actual type Vector
我认为这意味着sum
不知道如何操作Vector
。解决这个问题的最佳方法是什么?
答案 0 :(得分:6)
所以我注意到你没有使用标准矢量。我建议改用它们,但如果你真的不想,
toList :: Vector -> [Double]
toList (Vector a) = a
并使用
dot a b = sum . toList $ a * b
如果切换到标准矢量,则有3个选择
将您的Vector
转到列表
import Data.Vector as V
dot a b = sum . V.toList $ a * b
简单,但不必要地慢。
使用更通用的sum
import Data.Foldable as F
dot a b = F.sum $ a * b
灵活,可能导致奇怪的类型错误,因为我们依赖于另一个类型类。
使用不同的,特定的(奇特的单词是单态的)sum
import Data.Vector as V
dot a b = V.sum $ a * b
最简单,但当然,如果你停止使用矢量,这将会破坏。
我建议选项3,不需要过于笼统。
答案 1 :(得分:3)
一般来说,是的。通常这是可取的,因为它允许您删除不希望人们访问的功能。例如,给予用户[Double]
可以让他们计算长度并将其作为链接列表进行检查,而newtype Vector = Vector [Double]
会让vectorLength
公开Vector
当且仅当您认为这是一个好主意时
但那不是手头的问题。您希望能够在Vector
类型上运行,而无需重新定义您能想到的所有有用功能。幸运的是,有很多方法可以解决这个问题。
您可以将type
定义为Vector
同义词,而不是新的具体类型。这让Haskell透明地将[Double]
解释为type Vector = [Double]
vectorSum :: Vector -> Double
vectorSum = sum
并自动使用完整的列表函数
vectorSum
你可以,虽然你试图避免它,但也可以直接写自己的vectorSum :: Vector -> Double
vectorSum (Vector list) = sum list
。
Vector
一般来说,它在实际代码中看起来有点不同,因为人们倾向于滥用记录语法来为data Vector = Vector { unVector :: [Double] }
vectorSum :: Vector -> Double
vectorSum = sum . unVector
manySums :: [Double]
manySums = map (\v -> sum (unVector v)) makeLotsOfVectors
轻松“逃避”
Vector
您可以将Foldable
定义为Foldable
的实例。 t
是一个类型类,是Haskell实现多态的主要机制。特别是,如果您认为类型Foldable
是包含可以“粉碎”在一起的特定顺序的元素,那么Vector
类型就是sum
的实例。这几乎描述了import Prelude hiding (foldl)
import Data.Foldable (Foldable, foldl, foldMap)
data Vector a = Vector [a] -- note that the type is parametric, this is
-- required for Foldable
foldableSum :: (Foldable t) => t Double -> Double
foldableSum = foldl (+) 0
instance Foldable Vector where
foldMap f (Vector list) = foldMap f list -- it just inherits from the
-- Foldable [] instance
vectorSum :: Vector Double -> Double
vectorSum = foldableSum
和GeneralizedNewtypeDeriving
,所以
Vector
您还可以使用一种非常方便的GHC Haskell机制[]
来使这些繁琐的实例自动发生。要做到这一点,我们必须注意newtype
非常类似于data
---它实际上只是一个新名称。这意味着我们可以使用{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Vector a = Vector [a] deriving ( Foldable )
vectorSum :: Vector Double -> Double
vectorSum = foldl (+) 0
代替Foldable
。
GeneralizedNewtypeDeriving
有趣的是,GHC Haskell还有一个扩展,即使您没有新类型,也可以派生Foldable
。 {-# LANGUAGE DeriveFoldable #-}
data Vector a = Vector [a]
vectorSum :: Vector Double -> Double
vectorSum = foldl (+) 0
更强大,但对于vector
,我们不需要使用它。
{{1}}
还有其他人提到的非常强大的{{1}}库可以完成所有这些以及更多功能。
答案 2 :(得分:2)
您使用的是Prelude中的sum,类型为:
sum :: Num a => [a] -> a
向量的总和在Data.Vector中定义(通常是导入的限定)
编辑:我错过了你使用自己的数据类型的事实,而不是Data.Vector中的数据类型
答案 3 :(得分:2)
由于您没有使用Data.Vector
向量,因此实际上无法使sum
直接对您的数据类型起作用,因为它的类型是
sum :: Num a => [a] -> a
并且您提供Vector [Double]
而不是Num a => [a]
。你必须首先在向量中提取列表:
toList :: Vector -> [Double]
toList (Vector vals) = vals
dot :: Vector -> Vector -> Double
dot a b = sum . toList $ a * b
话虽如此,您应该只使用Data.Vector
提供的向量,或者至少应将Vector
类型定义为
{-# LANGUAGE DeriveFunctor #-}
import Control.Applicative
data Vector a = Vector [a] deriving (Eq, Ord, Show, Functor)
instance Applicative Vector where
pure a = Vector [a]
(Vector fs) <*> (Vector xs) = Vector $ zipWith ($) fs xs
instance Num a => Num (Vector a) where
a + b = (+) <$> a <*> b
a * b = (*) <$> a <*> b
-- etc.
然后你可以拥有Vector Int
,Vector Double
,甚至Vector (Int -> Double)
,因为现在它是Functor
和Applicative
,你可以做更多的事情正如这个例子所暗示的那样。
答案 4 :(得分:1)
创建一个你可以做的点功能
data Vector = Vector [Double]
dot :: Vector -> Vector -> Double
dot (Vector a) (Vector b) = sum $ zipWith (*) a b
这样'a'和'b'现在是Vector内部的[Double],而不是Vector的自我。
步骤一步:
dot (Vector [1,2]) (Vector [3,4]) = sum $ zipWith (*) [1,2] [3,4]
= sum $ zipWith (*) [1,2] [3,4]
= sum $ [1*3, 2*4]
= 1*3 + 2*4
= 3 + 8
= 11