简体   繁体   English

如何返回由交替组合 Haskell 编程中的两个函数列表产生的函数?

[英]How to return a function resulted from alternate combining two lists of functions in Haskell programming?

In my Haskell course assignment, I want to cope a challenge which is essentially to catch on how to process lists of functions.在我的 Haskell 课程作业中,我想应对一个挑战,这个挑战本质上是了解如何处理函数列表。 The input consists of two such lists, lets say first of them is given as [A0..AN] and second one is told to be [B0..BN].输入由两个这样的列表组成,假设第一个列表为 [A0..AN],第二个列表为 [B0..BN]。 I need to return a single function that composes functions applying them so that functions alternate as nested function arguments inside an output function.我需要返回一个函数,该函数由应用它们的函数组成,以便函数在输出函数中交替作为嵌套函数参数。 In fact, it means the output like one:实际上,这意味着输出如下:

G x = A0 (B0 (A1 (B1 (A2 (B2 (...AN (BN)))))) G x = A0 (B0 (A1 (B1 (A2 (B2 (...AN (BN))))))

My primary solution doesn't work, there is code I've tried to start with:我的主要解决方案不起作用,我尝试从以下代码开始:

funcCarnage :: [a -> a] -> [a -> a] -> (a -> a)
funcCarnage (x:xs) (y:ys) = extraFuncCarnage xs ys x 2 where
extraFuncCarnage bs fs gs n
    | length bs == 0 && length fs == 0 = gs  
    | odd n = extraFuncCarnage (tail bs) fs (gs $ head bs) (n + 1) 
    | otherwise = extraFuncCarnage bs (tail fs) (gs $ head fs) (n + 1) 

Compilation fails with erro messages:编译失败并显示错误消息:

* Occurs check: cannot construct the infinite type: a ~ a -> a
  Expected type: a -> a
    Actual type: (a -> a) -> a -> a
* In the expression: extraFuncCarnage xs ys x 2
  In an equation for `funcCarnage':
      funcCarnage (x : xs) (y : ys)
        = extraFuncCarnage xs ys x 2
        where
            extraFuncCarnage bs fs gs n
              | length bs == 0 && length fs == 0 = gs
              | odd n = extraFuncCarnage (tail bs) fs (gs $ head bs) (n + 1)
              | otherwise = extraFuncCarnage bs (tail fs) (gs $ head fs) (n + 1)

* Occurs check: cannot construct the infinite type: t1 ~ t -> t1
  Expected type: [t] -> [t] -> t1 -> a1 -> t -> t1
    Actual type: [t] -> [t] -> (t -> t1) -> a1 -> t -> t1
* In an equation for `funcCarnage':
      funcCarnage (x : xs) (y : ys)
        = extraFuncCarnage xs ys x 2
        where
            extraFuncCarnage bs fs gs n
              | length bs == 0 && length fs == 0 = gs
              | odd n = extraFuncCarnage (tail bs) fs (gs $ head bs) (n + 1)
              | otherwise = extraFuncCarnage bs (tail fs) (gs $ head fs) (n + 1)

It's some obscure complaint, so I'm confused how to proceed with solving.这是一些晦涩的投诉,所以我很困惑如何继续解决。 How do I have to fix both type errors?我该如何修复这两种类型的错误?

You can first merge the two lists and then do a right fold.您可以先合并两个列表,然后进行右折叠。

import Control.Applicative(ZipList(..))

funcCarnage fs gs = foldr (.) id list
  where
    list = concat $ getZipList $ (\x y -> [x, y]) <$> ZipList fs <*> ZipList gs 

The other answers discuss how to use library functions as building blocks to create your function.其他答案讨论了如何使用库函数作为构建块来创建您的函数。 I think it might also be interesting to see how to write the recursion yourself;我认为看看如何自己编写递归也可能很有趣; in this case it's particularly simple.在这种情况下,它特别简单。 I will follow luqui's excellent suggestion in the comments of first writing a function to interleave two lists, then composing all the functions in the result.我将遵循 luqui 在评论中的出色建议,首先编写一个函数来交错两个列表,然后在结果中组合所有函数。

interleave :: [a] -> [a] -> [a]
interleave (x:xs) ys = x : interleave ys xs
interleave [] ys = ys

compose :: [a -> a] -> a -> a
compose [] = id
compose (f:fs) = f . compose fs

Then your function can run these two in sequence:然后你的函数可以按顺序运行这两个:

carnage :: [a -> a] -> [a -> a] -> a -> a
carnage fs gs = compose (interleave fs gs)

Later, as you get more advanced, you may be able to notice certain common looping structures in the above code.稍后,随着您变得更高级,您可能会注意到上述代码中某些常见的循环结构。 The compose one in particular is quite a standard "shape" of iteration: do something to combine a particular element with the result of making a recursive call.特别是compose是非常标准的迭代“形状”:做一些事情将特定元素与进行递归调用的结果结合起来。 This shape is so common that we have a standard library function to encapsulate it.这种形状很常见,我们有一个标准库函数来封装它。 foldr takes the combining function (that combines a single element with the result of a recursive call) and the base case result as arguments, then does the iteration, so: foldr将组合函数(将单个元素与递归调用的结果组合在一起)和基本情况结果作为参数,然后进行迭代,因此:

compose :: [a -> a] -> a -> a
compose = foldr (.) id

The loop in interleave is a less common shape, although it does come up occasionally in various circumstances. interleave中的循环是一种不太常见的形状,尽管它偶尔会在各种情况下出现。 One way is to think about it as a zip;一种方法是将其视为 zip; or, you can observe that matrix transposition is essentially an arbitrary-arity zip and reuse that operation.或者,您可以观察到矩阵转置本质上是一个任意数量的 zip 并重用该操作。 So:所以:

interleave :: [a] -> [a] -> [a]
interleave xs ys = concat (transpose [xs, ys])

At this point, the function implementations for compose and interleave are so short, that you might consider inlining them if you don't find the names to be an important part of the explanatory power of the code.此时, composeinterleave的函数实现非常短,如果您发现名称不是代码解释力的重要组成部分,您可能会考虑内联它们。 So:所以:

carnage :: [a -> a] -> [a -> a] -> a -> a
-- was: carnage fs gs = compose (interleave fs gs)
carnage fs gs = foldr (.) id (concat (transpose [fs, gs]))

Idiomatically, Haskell programmers typically prefer function composition chains to nested parentheses:习惯上,Haskell 程序员通常更喜欢函数组合链而不是嵌套的括号:

carnage fs gs = foldr (.) id . concat . transpose $ [fs, gs]

To my experienced eye, this looks compact and readable;以我经验丰富的眼光来看,这看起来紧凑且可读; I would stop there.我会停在那里。

I used this我用这个

funcCarnage :: [a -> a] -> [a -> a] -> (a -> a)
funcCarnage xs ys = foldl1 (.) $ zipWith (.) xs ys

Not convinced if I used the right fold.不确定我是否使用了正确的折叠。

Applying正在申请

funcCarnage [(+) 10, (*) 2] [(+) 3, (*) 5] $ 9

gives 103 .给出103

See ZVON for some info about (.) .有关(.)的一些信息,请参见ZVON

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM