하스켈 입문서의 샘플코드에서 $로 되어 있는 것을 .로 바꾸는 과정에서 에러가 나고 있습니다.
아래 프로그램의 어떤 부분을 고쳐야 할까요?
main = do cs <- getContents
putStr $ lastNLines 10 cs
lastNLines :: Int -> String -> String
lastNLines n = unlines.(takeLast n).lines
takeLast :: Int -> String -> String
takeLast n = reverse.(take n).reverse
tail.hs:5:38:
Couldn't match expected type `String'
against inferred type `String -> [String]'
Probable cause: `lines' is applied to too few arguments
In the second argument of `($)', namely `lines'
In the second argument of `($)', namely `(takeLast n) $ lines'
라는 에러 메세지가 나옵니다.
고수님의 가르침 부탁드립니다.
아울러 좀더 하스켈 스런 프로그램이라면 어떻게 해야 할까요?
미리 감사드립니다.
1. 들여쓰기가 보이지
1. 들여쓰기가 보이지 않아서 본문을 수정했습니다. [code]와 [/code]로 코드를 감싸면 고친 것처럼 예쁘게 출력이 됩니다.
2. lines는 String -> [String]입니다. 그러니까 takeLast도 Int -> String -> String이 아니라 Int -> [String] -> [String]이 되어야 합니다.
그런데 하스켈의 장점을 살리려면 굳이 takeLast를 Int -> [String] -> [String]로 하는 것보다 Int -> [a] -> [a]로 하는 것이 낫습니다.
한 걸음 더 나아가 하스켈의 자료형 추론 기능을 활용하면 알아서 Int -> [a] -> [a]로 추정해주므로 자료형을 일일이 지정해줄 필요없이 그냥 코드만 적으면 에러가 나지 않습니다.
3. 사실 간단한 코드가 아니면 가급적 .을 쓰지 않는 게 좋습니다. takeLast n = reverse.(take n).reverse라고 하는 것보다 takeLast n list = reverse $ take n $ reverse list라고 하는 편이 훨씬 읽기가 좋지요.
그런데 takeLast의 지금 구현 방식은 리스트를 두 번이나 뒤집는 방식이라 별로 좋지가 않습니다. 짧은 코드면 모르겠지만 길면 성능이 문제가 되겠죠. 차라리 drop을 이용해서 구현하는 편이 나을 것 같습니다.
감사합니다!
감사합니다.
말씀대로 형을 지정해 주지 않았더니 에러없이 컴파일이 되었습니다.
제가 지정하는 형에 문제가 있는 것 같습니다.
공부를 위해서 형을 이리저리 지정해 주는데 역시 문제가 생깁니다.
main = do cs <- getContents putStr $ lastNLines 10 cs lastNLines :: Int -> [a] -> [a] lastNLines n cs = unlines $ takeLast n $ lines cs takeLast :: Int -> [a] -> [a] takeLast n ss = reverse $ take n $ reverse ss에러메세지는
tail.hs:7:0: Invalid type signature
라고 나옵니다.
추가로 제가 drop함수를 몰라서 코드를 말씀주신대로 고쳐보지는 못하였습니다.
답변에 다시 감사드립니다.
drop을 이용해 보았습니다.
main = do cs <- getContents putStr $ lastNLines 10 cs lastNLines n cs = unlines $ takeLast n $ lines cs takeLast n ss = drop (length ss - n) ss좀더 하스켈스런 코드라면 어떻게 하실지 가르침 부탁드립니다.
귤님의 조언은
귤님의 조언은 "takeLast의 타입이 Int→String→String이 아닌 Int→[String]→[String]이어야하며, 보다 범용적으로는 Int→[a]→[a]로 일반화할 수 있다"는 뜻이었습니다. 함께 수정하신 lastNLines 함수의 타입은 Int→String→String이어야만 하며 이보다 일반화될 수 없습니다. (lines 함수의 정의역이 String이므로 (lastNLines n)의 정의역이 String으로 확정되고, unlines 함수의 공변역이 String이므로 (lastNLines n)의 공변역도 String으로 확정됩니다.)
main = do cs <- getContents putStr $ lastNLines 10 cs lastNLines :: Int -> String -> String lastNLines n cs = unlines $ takeLast n $ lines cs takeLast :: Int -> [a] -> [a] takeLast n ss = reverse $ take n $ reverse ss감사합니다.
친절하신 답변에 진심으로 감사드립니다. 책을 읽어나가면서 잘 이해가 되지 않던 부분이 선생님의 조언으로 해소가 되는 느낌입니다. 다시 한번 감사드립니다.