【发布时间】:2014-06-12 22:23:08
【问题描述】:
我有两个不同类型签名的函数:
f :: Int -> [a] -> [a]
g :: (Int, Int) -> [a] -> [a]
第一个参数确定函数操作的列表的长度。例如,f 可能对长度为 p-1 的列表进行操作,g 对长度为 p^(e-1) 的列表分别针对第一个参数 p 和 (p,e) 进行操作。
我使用f 和g 作为另一个函数h :: Int -> ([a] -> [a]) -> [a] -> [a] 的参数,它需要知道这个长度函数(h 的第一个参数)。我发现自己目前正在做的是:
\p -> h (p-1) (f p)
\(p,e) -> h (p^(e-1)) (g (p,e))
我在任何地方都将h 与f 和g 结合使用。这种重复很容易出错并且很混乱。
我们的目标是找到一种方法来避免将长度参数传递给h。相反,h 应该能够根据函数参数确定长度本身。
一个非解决方案是将f 的定义更改为:
f' :: (Int, Int) -> [a] -> [a]
f' (2,_) = previous def
f' (p,_) = previous def
funcToLen :: ((Int, Int) -> [a] -> [a]) -> (Int, Int) -> Int
funcToLen f' (p,_) = p-1
funcToLen g (p,e) = p^(e-1)
h' :: (Int, Int) -> ((Int, Int) -> [a] -> [a]) -> [a] -> [a]
h' (p,e) func xs = let len = funcToLen func
func' = func (p,e)
in previous def
-- usage
(\p -> h' (p,??) f')
(\(p,e) -> h' (p,e) g)
这有几个缺点:
- 我要更改
f的第一个参数,然后忽略元组的第二部分 - 当我实际使用
h'和f'时,我必须为元组的第二部分创建一个虚拟参数 - 最重要的是,
funcToLen不起作用,因为我无法对函数名称进行模式匹配。
另一个实际可行的解决方案是使用Either:
f' :: Int -> (Int, [a] -> [a])
f' 2 xs = (1, previous def)
f' p xs = (p-1, previous def)
g' :: (Int, Int) -> Either Int ([a] -> [a])
g' (p,1) xs = (1, previous def)
g' (p,e) xs = (p^(e-1), previous def)
h' :: (Int, ([a] -> [a])) -> ([a] -> [a])
h' ef = let len = fst ef
f = snd ef
in previous def
这也有一些缺点:
- 长度函数为
f'和g'的每个模式重复 -
f'、g'、h'的类型签名都比较丑 - 我不能立即单独使用
f'和g'(即不能作为h'的参数)。相反,我必须剥离元组。
我正在寻找清理这个问题的方法,这样我就不必在任何地方复制长度函数,而且还允许我以预期的方式使用函数 f 和 g'。我希望这个问题之前已经“解决”了,但我不太清楚我应该寻找什么。
【问题讨论】:
-
究竟为什么你需要这种凌乱的“操作长度”的传递?听起来最好拆分列表并将一些部分传递给对列表all进行操作的函数。
-
我无法理解您的要求。不可能对函数进行模式匹配,并且在您对
funcToLen的定义中,第一个模式总是会成功,而在第二个示例中,您使用Either作为构造函数(您的意思是Left/Right?) 不管怎样,周围有这么多随机的单字符变量,很难理解。我很难理解为什么这是必要的。无论如何,由于您有两种不同的类型,并且需要对它们进行模式匹配,因此使用Either似乎是最好的选择。 -
@leftaroundabout 总之,数学。列表输入是一个张量,
f和g在该张量的一维上运行。函数h“提升”f和g从一维到多维。结果是f和g最终只处理列表中的几个元素,它们之间有一个步幅和一个偏移量。h需要知道f的维度,以便知道运行f的次数(以及步幅/偏移量)。 -
我显然严重滥用了
Eithermonad。我将用元组替换那些...... -
您使用普通列表来表示张量?那是……大胆。
Data.Tensor有什么问题?如果不是像这样预先构建的东西,您至少应该包装一个自定义类型来正确处理尺寸问题。
标签: haskell higher-order-functions