2

我正在使用 Haskell 并已将 a 声明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. 解决这个问题的最佳方法是什么?

4

5 回答 5

6

所以我注意到你没有使用标准向量。我建议切换到他们,但如果你真的不想,

 toList :: Vector -> [Double]
 toList (Vector a) = a

并使用

dot a b = sum . toList $ a * b

如果您切换到标准向量,您有 3 个选择

  1. 把你Vector的清单,

    import Data.Vector as V
    dot a b = sum . V.toList $ a * b
    

    简单,但不必要的慢。

  2. 使用更通用的sum

    import Data.Foldable as F
    dot a b = F.sum $ a * b
    

    灵活,可能会导致奇怪的类型错误,因为我们依赖于另一个类型类。

  3. 使用不同的、具体的(花哨的词是单态的)sum

     import Data.Vector as V
     dot a b = V.sum $ a * b
    

    最简单,但当然,如果你停止使用向量,这将会中断。

我推荐选项 3,不需要过于笼统。

于 2013-10-30T19:42:06.553 回答
3

一般来说,是的。通常这是可取的,因为它允许您删除您不希望人们访问的功能。例如,给用户[Double]让他们计算长度并将其作为链接列表进行检查,而当且仅当您认为这是一个好主意时,您才newtype Vector = Vector [Double]可以公开。vectorLength

但这不是手头的问题。您立即希望能够对您的Vector类型进行操作,而无需重新定义您能想到的每一个有用的功能。幸运的是,有很多方法可以解决这个问题。

您可以定义Vectortype同义词而不是新的具体类型。这让 Haskell 可以透明地解释Vector[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

您可以定义VectorFoldable. Foldable是一个类型类,是 Haskell 实现多态性的主要机制。特别是,您说类型t是一个实例,Foldable如果您可以将其视为包含可以“粉碎”在一起的特定顺序的元素。这几乎描述了 aVector和 a 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

您还可以使用 GHC Haskell 调用的一种非常方便的机制GeneralizedNewtypeDeriving来使这些繁琐的实例自动发生。为此,我们必须注意它Vector---它实际上只是它的一个新名称。这意味着我们可以使用而不是.[]newtypedata

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Vector a = Vector [a] deriving ( Foldable )

vectorSum :: Vector Double -> Double
vectorSum = foldl (+) 0

有趣的是,GHC Haskell 还有一个扩展,Foldable即使您没有新类型,也可以派生。GeneralizedNewtypeDeriving更强大,但Foldable具体来说我们不需要使用它。

{-# LANGUAGE DeriveFoldable #-}

data Vector a = Vector [a]

vectorSum :: Vector Double -> Double
vectorSum = foldl (+) 0

还有其他人提到的非常强大的vector库,它可以完成所有这些以及更多。

于 2013-10-30T23:32:05.513 回答
2

您正在使用 Prelude 中的 sum 类型:

sum :: Num a => [a] -> a

向量的总和在 Data.Vector 中定义(通常导入合格)

编辑:我错过了您使用自己的数据类型的事实,而不是来自 Data.Vector 的数据类型

于 2013-10-30T19:41:55.217 回答
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, even Vector (Int -> Double),因为它现在是 aFunctor和 an Applicative,你可以用它做更多的事情,正如这个例子所暗示的那样。

于 2013-10-30T20:18:42.450 回答
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
于 2013-10-31T04:22:01.880 回答