这个错误的原因是什么?为什么我认为它违反了引用透明度?

Mik*_*H-R 1 haskell

很抱歉问这个问题,但它看起来像是打破了参考透明度.

在探索问题并将其分解时(问题是获取列表列表的对角元素)我想出了这个(正确工作)解决方案:

import Data.List

nums = [[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,16]]

southwest = transpose . zipWith (drop) [0..]
southwest2 = transpose . zipWith (drop) [0..] . transpose

my_ans = southwest nums ++ southwest2 nums
Run Code Online (Sandbox Code Playgroud)

(注意它包含两次中间行,这对我的用例来说不是问题)

现在很明显,很容易被重构.在第一次尝试以无点样式编写它之后,我认为通过这些尝试简单地编写它是非易点的:

my_ans2 x = (diagFunc x) ++ (diagFunc . transpose x)
              where diagFunc = transpose . zipWith (drop) [0..]

my_ans3 x = concat [(diagFunc x), (diagFunc . transpose x)]
              where diagFunc = transpose . zipWith (drop) [0..]
Run Code Online (Sandbox Code Playgroud)

现在这些编译和作品都不会让我感到困惑,因为在我看来,这会破坏参照透明度.有人可以解释为什么我错了,以及如何正确地编写这个函数(额外的点是以无点的方式编写它,因为我不能).

作为参考,错误在这里:

/home/michael/scripts/temp.hs:14:30:
    Couldn't match expected type ‘[[a]]’
                with actual type ‘a0 -> [[a1]]’
    Relevant bindings include
      x :: [[a]]
        (bound at /home/michael/scripts/project_euler/temp.hs:14:9)
      my_ans2 :: [[a]] -> [[a]]
        (bound at /home/michael/scripts/project_euler/temp.hs:14:1)
    In the second argument of ‘(++)’, namely ‘(diagFunc . transpose x)’
    In the expression: (diagFunc x) ++ (diagFunc . transpose x)
    In an equation for ‘my_ans2’:
        my_ans2 x
          = (diagFunc x) ++ (diagFunc . transpose x)
          where
              diagFunc = transpose . zipWith (drop) [0 .. ]

/home/michael/scripts/temp.hs:14:41:
    Couldn't match expected type ‘a0 -> [[a1]]’
                with actual type ‘[[a]]’
    Relevant bindings include
      x :: [[a]]
        (bound at /home/michael/scripts/project_euler/temp.hs:14:9)
      my_ans2 :: [[a]] -> [[a]]
        (bound at /home/michael/scripts/project_euler/temp.hs:14:1)
    Possible cause: ‘transpose’ is applied to too many arguments
    In the second argument of ‘(.)’, namely ‘transpose x’
    In the second argument of ‘(++)’, namely ‘(diagFunc . transpose x)’
Run Code Online (Sandbox Code Playgroud)

再一次,我非常肯定我错了,我只需要有人指出如何.:) 提前致谢.

Lee*_*Lee 9

你可以修复它:

my_ans2 x = (diagFunc x) ++ (diagFunc . transpose $ x)
              where diagFunc = transpose . zipWith (drop) [0..]

my_ans3 x = concat [(diagFunc x), (diagFunc . transpose $ x)]
              where diagFunc = transpose . zipWith (drop) [0..]
Run Code Online (Sandbox Code Playgroud)

问题是diagFunc . transpose x解析为diagFunc . (transpose x),而不是(diagFunc . transpose) x你想要的.

您可以使用函数的monoid实例以无点样式编写它:

import Data.Monoid

my_ans2' = diagFunc `mappend` (diagFunc . transpose)
  where diagFunc = transpose . zipWith (drop) [0..]
Run Code Online (Sandbox Code Playgroud)

函数的monoid实例可用于返回monoidal值的函数.由于名单是幺那里mappend= (++)你可以在这里使用它.它被定义为:

instance Monoid b => Monoid (a -> b) where
    mempty _ = mempty
    mappend f g x = f x `mappend` g x
Run Code Online (Sandbox Code Playgroud)

所以mappend对于函数fg应用f以及g参数,x并结合使用mappendmonoid类型的结果b.b这是一个列表,所以你最终得到了

my_ans2' = (f x) ++ (g x)
Run Code Online (Sandbox Code Playgroud)

这里fdiagFuncgdiagFunc . transpose.

正如@chi在评论中指出的那样,你也可以使用一个应用解决方案:

import Control.Applicative

my_ans2'' = (++) <$> diagFunc <*> (diagFunc . transpose)
  where diagFunc = transpose . zipWith (drop) [0..]
Run Code Online (Sandbox Code Playgroud)

这是更一般的,因为它不依赖于两个函数的返回类型是幺半群.