Напишите функцию отношения транзитивного замыкания в Haskell

#haskell #functional-programming

Вопрос:

Предполагается, что я напишу функцию, возвращающую функцию транзитивного замыкания отношения. Вот что я написал до сих пор:

 relTrans :: [(Integer, Integer)] -> [(Integer, Integer)]
relTrans rel  |  rel == remove_dups(rel    relComp (remove_dups rel) (remove_dups rel))     =     rel
              |  otherwise                                                                  =     relTrans remove_dups(rel    relComp (remove_dups rel) (remove_dups rel))
 

Функции, упомянутые в этом как remove_dups и relComp, следующие —

 relComp :: [(Integer, Integer)] -> [(Integer, Integer)] -> [(Integer, Integer)]
relComp r1 r2 = [(a,c) | (a,k1)<-r1, (k2,c)<-r2, k1 == k2]

remove_dups :: [a] -> [a]
remove_dups [] = []
remove_dups (x:xs) = x : remove_dups (removeElem x XS)

removeElem :: a -> [a] -> [a]
removeElem n [] = []
removeElem n (m:zs) = if (n == m) then removeElem n zs else m:(removeElem n zs)
 

Однако я продолжаю получать следующую ошибку :-

     * Couldn't match expected type `[(Integer, Integer)]
                                    -> [(Integer, Integer)]'
                  with actual type `[(Integer, Integer)]'
    * The function `relTrans' is applied to two arguments,
      but its type `[(Integer, Integer)] -> [(Integer, Integer)]'
      has only one
      In the expression:
        relTrans
          remove_dups (rel    relComp (remove_dups rel) (remove_dups rel))
      In an equation for `relTrans':
          relTrans rel
            | rel
                == remove_dups (rel    relComp (remove_dups rel) (remove_dups rel))
            = rel
            | otherwise
            = relTrans
                remove_dups (rel    relComp (remove_dups rel) (remove_dups rel))
    |       
183 |               |  otherwise                                                                  =     relTrans remove_dups(rel    relComp (remove_dups rel) (remove_dups rel))
    |                                                                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 

И эта ошибка —

     * Couldn't match expected type `[(Integer, Integer)]'
                  with actual type `[a0] -> [a0]'
    * Probable cause: `remove_dups' is applied to too few arguments
      In the first argument of `relTrans', namely `remove_dups'
      In the expression:
        relTrans
          remove_dups (rel    relComp (remove_dups rel) (remove_dups rel))
      In an equation for `relTrans':
          relTrans rel
            | rel
                == remove_dups (rel    relComp (remove_dups rel) (remove_dups rel))
            = rel
            | otherwise
            = relTrans
                remove_dups (rel    relComp (remove_dups rel) (remove_dups rel))
    |       
183 |               |  otherwise                                                                  =     relTrans remove_dups(rel    relComp (remove_dups rel) (remove_dups rel))
    |                                                                                                            ^^^^^^^^^^^
 

Может кто-нибудь, пожалуйста, помочь мне понять, почему я получаю эти ошибки, пожалуйста?

Ответ №1:

Похоже, вы пытаетесь использовать приложение функций в стиле C здесь:

   relTrans remove_dups(rel    relComp (remove_dups rel) (remove_dups rel))
  --             here ^
 

В Haskell , который применяет relTrans функцию к двум аргументам: remove_dups и (rel ...) , но relTrans имеет только один аргумент, поэтому вы получаете ошибку.

Вы, вероятно, собирались написать это:

   relTrans (remove_dups (rel    relComp (remove_dups rel) (remove_dups rel)))
 

Комментарии:

1. Большое спасибо! Теперь он работает нормально! Это действительно открыло мне глаза, так как я не был так осторожен в использовании скобок в Haskell, как обычно в других языках программирования, и это доказало, как что-то настолько тривиальное может так сильно повлиять на мой код! 🙂

2. @user202004 К сожалению, размещение скобок (в большинстве языков программирования) на самом деле довольно фундаментально, а не тривиально! Возможно, лишь немного менее фундаментальный, чем синтаксис для вызова функций/процедур и передачи им аргументов. Вам действительно нужно понять, как каждый используемый вами язык выражает это, иначе компилятор/интерпретатор просто не увидит структуру вызова, которую вы намеревались, и поэтому у него нет надежды выяснить, что вы имели в виду.