Длина Haskell и фильтр для определения выпуклости или вогнутости линии

Я читаю Real World Haskell, пытаясь решить Ch3, Q10 используя ghc онлайн.

Пока у меня есть следующий код:

data Direction point = MyLeft point | MyRight point | Straight deriving (Show)

getDirectionFromTriple :: Direction p -> Direction p -> Direction p -> Direction p
getDirectionFromTriple p1 p2 p3 
  | (length . filter (== MyLeft) [p1, p2, p3]) > 1 = MyLeft p3
  | (length . filter (== MyRight) [p1, p2, p3]) > 1 = MyRight p3
  | otherwise = Straight

Я получаю следующую ошибку при попытке скомпилировать этот код (опубликованы только части, одна и та же ошибка появляется несколько раз):

[1 of 1] Compiling Main             ( jdoodle.hs, jdoodle.o )


jdoodle.hs:17:15: error:
    * Couldn't match expected type `a0 -> t0 a1'
                  with actual type `[point0 -> Direction point0]'
    * Possible cause: `filter' is applied to too many arguments
      In the second argument of `(.)', namely
        `filter (== MyLeft) [p1, p2, p3]'
      In the first argument of `(>)', namely
        `(length . filter (== MyLeft) [p1, p2, p3])'
      In the expression: (length . filter (== MyLeft) [p1, p2, p3]) > 2
   |
17 |   | (length . filter (== MyLeft) [p1, p2, p3]) > 2 = MyLeft p3
   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

jdoodle.hs:17:35: error:
    * Couldn't match expected type `point0 -> Direction point0'
                  with actual type `Direction p'
    * In the expression: p1
      In the second argument of `filter', namely `[p1, p2, p3]'
      In the second argument of `(.)', namely
        `filter (== MyLeft) [p1, p2, p3]'
    * Relevant bindings include
        p3 :: Direction p (bound at jdoodle.hs:16:30)
        p2 :: Direction p (bound at jdoodle.hs:16:27)
        p1 :: Direction p (bound at jdoodle.hs:16:24)
        getDirectionFromTriple :: Direction p
                                  -> Direction p -> Direction p -> Direction p
          (bound at jdoodle.hs:16:1)
   |
17 |   | (length . filter (== MyLeft) [p1, p2, p3]) > 2 = MyLeft p3
   |                                   ^^

jdoodle.hs:17:39: error:
    * Couldn't match expected type `point0 -> Direction point0'
                  with actual type `Direction p'
    * In the expression: p2
      In the second argument of `filter', namely `[p1, p2, p3]'
      In the second argument of `(.)', namely
        `filter (== MyLeft) [p1, p2, p3]'
    * Relevant bindings include
        p3 :: Direction p (bound at jdoodle.hs:16:30)
        p2 :: Direction p (bound at jdoodle.hs:16:27)
        p1 :: Direction p (bound at jdoodle.hs:16:24)
        getDirectionFromTriple :: Direction p
                                  -> Direction p -> Direction p -> Direction p
          (bound at jdoodle.hs:16:1)
   |
17 |   | (length . filter (== MyLeft) [p1, p2, p3]) > 2 = MyLeft p3
   |                                       ^^

Я был бы признателен либо за предложения, как исправить мой код, либо за предложения более кратких решений, определяющих доминирующее направление из тройки точек.


person mushishi    schedule 01.10.2019    source источник
comment
Среди прочих ошибок: начиная с p3 :: Direction p, MyLeft p3 :: Direction (Direction p), что не того типа.   -  person chi    schedule 01.10.2019
comment
В стороне: length xs > 1 пересекает весь позвоночник xs, потому что Int строгий; здесь это не имеет значения, но если ожидается, что length xs будет большим (или бесконечным), вы можете сделать это более эффективным, используя, например, not (small xs), где small = null . drop 1   -  person Jon Purdy    schedule 02.10.2019


Ответы (2)


Вы можете создавать функции, которые сообщают вам, правый он или левый, вместо использования ==

import Data.List 

data Direction point = MyLeft point | MyRight point | Straight deriving (Show)

getDirectionFromTriple :: Direction p -> Direction p -> Direction p -> Direction p
getDirectionFromTriple p1 p2 p3 
  | isDirection isLeft  [p1, p2, p3]  = MyLeft (getValue p3)
  | isDirection isRight [p1, p2, p3]  = MyRight (getValue p3)
  | otherwise = Straight

isDirection :: (Direction p -> Bool) -> [Direction p] -> Bool
isDirection f ps = (length . filter f) ps > 1

getValue (MyLeft a) = a
getValue (MyRight a) = a
getValue Straight  = error "No value in Straight"

isLeft  (MyLeft _)  = True
isLeft   _          = False
isRight (MyRight _) = True
isRight  _          = False

main = do
  putStrLn $ show $ getDirectionFromTriple (MyRight 2) Straight (MyLeft 1)
  putStrLn $ show $ getDirectionFromTriple (MyRight 2) (MyRight 3) (MyLeft 1)
  putStrLn $ show $ getDirectionFromTriple (MyLeft 1) (MyLeft 1) (MyRight 2) 
person Damián Rafael Lattenero    schedule 01.10.2019

То же, что и недавний вопрос Почему нечетный.fst не работает с функцией фильтра?. Когда вы пишете

length . filter f xs

это анализируется как

length . (filter f xs)

Это явно неправильно: filter f xs — это список, а не функция, поэтому нет смысла составлять его с помощью length. Вместо этого вы можете написать

(length . filter f) xs

хотя более популярным способом написания было бы

length . filter f $ xs
person amalloy    schedule 01.10.2019
comment
Ни одно из следующих действий не позволяет избежать вышеуказанной ошибки: (length . filter (== MyLeft) $ [p1, p2, p3]) (length . filter (== MyLeft) [p1, p2, p3]) ((length . filter ( == MyLeft)) [p1, p2, p3]) (длина фильтра $ (== MyLeft) [p1, p2, p3]) - person mushishi; 02.10.2019
comment
Эти предложения исправляют вашу ошибку первого типа, но не вторую (эта (== MyLeft) сама по себе не очень хорошая функция). - person amalloy; 02.10.2019