以通用方式作为实例的一组函数
Set of functions that are instances in a common way
我是 haskell 的新手,我想我陷入了一些面向对象的陷阱。这是我在实现时遇到问题的结构草图(简化):
Observable 作用于样本列表 (Int) 以产生结果 (Int) 的概念
- 一个概念 SimpleObservable,它使用特定模式实现结果(同时会有 Observables 以其他方式实现),例如平均水平
一个函数实例,例如一个只是平均值乘以常数
我的第一个想法是使用 subclass;类似的东西(下面有点做作,但希望能理解这个想法)
class Observable a where
estimate :: a -> [Int] -> Int
class (Observable a) => SimpleObservable a where
compute :: a -> Int -> Int
simpleEstimate :: a -> [Int] -> Int
simpleEstimate obs list = sum $ map compute list
data AveConst = AveConst Int
instance Observable AveConst where
estimate = simpleEstimate
instance SimpleObservable AveConst where
compute (AveConst c) x = c * x
然而,即使像上面这样的东西编译起来也很难看。谷歌搜索告诉我 DefaultSignatures
可能会有所帮助,因为我不必为每个实例都做 estimate = simpleEstimate
但从围绕它的讨论来看,这样做似乎是一种反模式。
另一种选择是没有子class,而是类似(具有相同的Observable
class):
data AveConst = AveConst Int
instance Observable AveConst where
estimate (AveConst c) list = sum $ map (*c) list
但是这样我不确定如何重用模式;每个 Observable
必须包含完整的 estimate
定义,并且会有代码重复。
第三种方式是带有函数字段的类型:
data SimpleObservable = SimpleObservable {
compute :: [Int] -> Int
}
instance Observable SimpleObservable where
estimate obs list =
sum $ map (compute obs) list
aveConst :: Int -> SimpleObservable
aveConst c = SimpleObservable {
compute = (*c)
}
但我也不确定这是否符合习惯。有什么建议吗?
我还没有完全理解这个问题,但我会在了解更多信息后编辑这个答案。
作用于列表并产生结果的东西可以简单地是一个函数。这个函数的接口(也就是类型)可以是[a] -> b
。这表示该函数接受某种类型的元素列表,并且 returns 可能是不同类型的结果。
现在,让我们发明一个小问题作为例子。我想要一个列表列表,列表上的一些函数产生一个数字,将这个函数应用于每个列表,然后 return 数字的平均值。
average :: (Fractional b) => ([a] -> b) -> [[a]] -> b
average f xs = sum (fmap f xs) / genericLength xs
例如,average genericLength
会告诉我子列表的平均长度。我不需要定义任何类型 类 或新类型。简单地说,我将函数类型 [a] -> b
用于那些将列表映射到某个结果的函数。
我建议更简单:
type Observable = [Int] -> Int
那么,平均可观察量是:
average :: Observable
average ns = sum ns `div` length ns
如果您的 Observable
需要一些内部数据——比如,一个常量乘以——没问题;这就是闭包的目的。例如:
sumTimesConst :: Int -> Observable
sumTimesConst c = sum . map (c*)
您可以毫不费力地抽象出 Observable
的构造;例如如果你想要一个只查看元素然后求和的SimpleObservable
,你可以:
type SimpleObservable = Int -> Int
timesConst :: Int -> SimpleObservable
timesConst = (*)
liftSimple :: SimpleObservable -> Observable
liftSimple f = sum . map f
那么 liftSimple . timesConst
是另一种拼写 sumTimesConst
.
的完美方式
...但老实说,我觉得做上述任何事情 都很脏。 sum . map (c*)
是一个完全可读的表达式,没有为其类型引入有问题的新名称。
我是 haskell 的新手,我想我陷入了一些面向对象的陷阱。这是我在实现时遇到问题的结构草图(简化):
Observable 作用于样本列表 (Int) 以产生结果 (Int) 的概念
- 一个概念 SimpleObservable,它使用特定模式实现结果(同时会有 Observables 以其他方式实现),例如平均水平
一个函数实例,例如一个只是平均值乘以常数
我的第一个想法是使用 subclass;类似的东西(下面有点做作,但希望能理解这个想法)
class Observable a where
estimate :: a -> [Int] -> Int
class (Observable a) => SimpleObservable a where
compute :: a -> Int -> Int
simpleEstimate :: a -> [Int] -> Int
simpleEstimate obs list = sum $ map compute list
data AveConst = AveConst Int
instance Observable AveConst where
estimate = simpleEstimate
instance SimpleObservable AveConst where
compute (AveConst c) x = c * x
然而,即使像上面这样的东西编译起来也很难看。谷歌搜索告诉我 DefaultSignatures
可能会有所帮助,因为我不必为每个实例都做 estimate = simpleEstimate
但从围绕它的讨论来看,这样做似乎是一种反模式。
另一种选择是没有子class,而是类似(具有相同的Observable
class):
data AveConst = AveConst Int
instance Observable AveConst where
estimate (AveConst c) list = sum $ map (*c) list
但是这样我不确定如何重用模式;每个 Observable
必须包含完整的 estimate
定义,并且会有代码重复。
第三种方式是带有函数字段的类型:
data SimpleObservable = SimpleObservable {
compute :: [Int] -> Int
}
instance Observable SimpleObservable where
estimate obs list =
sum $ map (compute obs) list
aveConst :: Int -> SimpleObservable
aveConst c = SimpleObservable {
compute = (*c)
}
但我也不确定这是否符合习惯。有什么建议吗?
我还没有完全理解这个问题,但我会在了解更多信息后编辑这个答案。
作用于列表并产生结果的东西可以简单地是一个函数。这个函数的接口(也就是类型)可以是[a] -> b
。这表示该函数接受某种类型的元素列表,并且 returns 可能是不同类型的结果。
现在,让我们发明一个小问题作为例子。我想要一个列表列表,列表上的一些函数产生一个数字,将这个函数应用于每个列表,然后 return 数字的平均值。
average :: (Fractional b) => ([a] -> b) -> [[a]] -> b
average f xs = sum (fmap f xs) / genericLength xs
例如,average genericLength
会告诉我子列表的平均长度。我不需要定义任何类型 类 或新类型。简单地说,我将函数类型 [a] -> b
用于那些将列表映射到某个结果的函数。
我建议更简单:
type Observable = [Int] -> Int
那么,平均可观察量是:
average :: Observable
average ns = sum ns `div` length ns
如果您的 Observable
需要一些内部数据——比如,一个常量乘以——没问题;这就是闭包的目的。例如:
sumTimesConst :: Int -> Observable
sumTimesConst c = sum . map (c*)
您可以毫不费力地抽象出 Observable
的构造;例如如果你想要一个只查看元素然后求和的SimpleObservable
,你可以:
type SimpleObservable = Int -> Int
timesConst :: Int -> SimpleObservable
timesConst = (*)
liftSimple :: SimpleObservable -> Observable
liftSimple f = sum . map f
那么 liftSimple . timesConst
是另一种拼写 sumTimesConst
.
...但老实说,我觉得做上述任何事情 都很脏。 sum . map (c*)
是一个完全可读的表达式,没有为其类型引入有问题的新名称。