본문 바로가기

Haskell

Applicative에 대해 함수를 사상할 수 있는 데이터 구조를 functor라 한다. 흔히 fmap 함수를 적용할 수 있는 타입을 말하는데, 기본적인 functor로는 리스트, Maybe 등이 있다. 이 때의 fmap의 타입은 a -> b 로 단인자 함수가 흔히 상정된다. 예를 들어 fmap (+1) [1,2,3] 이나 fmap (*2) (Just 3) 등의 식은 단인자 함수인 (+1), (*2) 를 functor에 맵핑하여 새로운 값을 만들어낸다. 만약 사상하는 함수가 단인자 함수가 아닌 경우라면 어떨까? fmap (*) (Just 3)이라는 표현식은 어떤 결과를 내놓을까? (아니면 그냥 펑하고 터지게 될 것인가?)하스켈의 함수는 기본적으로 항상 커링된다. 즉 어떤 함수의 타입이 (a, b) -> c 가 아닌 a -> b ->.. 더보기
기본적인 입출력과 모나드 대부분의 프로그래밍 언어는 입출력에 관련한 '함수'를 제공한다. 그리고 이런 함수들은 기본 중의 기본으로 취급되면서 '있는 그대로 쓰면 되는 것'으로 취급된다. 예를 들어 파이썬의 경우에 입출력함수는 input(), print()가 있다. 특히 input()과 같이 표준 입력을 받아오는 함수의 경우, 함수를 평가한 결과가 키보드등으로 입력된 문자열 값이 된다. 물론 하스켈의 경우에도 입출력 함수가 있다.(그것도 여러개) 하지만 다른 언어에서 가장 기본중의 기본이 되는 입출력이 하스켈에서는 그리 간단한 일이 아니다. 그것은 하스켈이 순수 함수형 언어라는 디자인 특성을 가지고 있기 때문인데, 따라서 입출력 자체는 어렵지 않은 일이나, 그것을 다루는 방법이 제법 까다롭다. 입출력을 담당하는 기본 함수 하스켈은.. 더보기
커스텀 타입을 정의하는 법 data 키워드는 새로운 데이터 타입을 정의할 때 사용된다. 새로운 타입을 정의하는 방법은 크게 두 가지가 있다. 열거형으로 정의하기 조합형으로 정의하기 열거형은 대수적 타입을 정의할 때 흔히 사용할 수 있다. data 타입명을 좌변으로 하고 등호의 우변에는 타입의 개별 값을 |로 구분지어 넣는다. 이 때 열거형의 개별 값은 인자를 받지 않는 constructor로 간주되므로 대문자로 작성한다. 예를 들어 Bool 타입은 다음과 같이 정의될 수 있다. data Bool = True | False 여기서 Bool 자체는 데이터 타입의 이름이며, True, False는 각 열거케이스에 해당하는데 이 각각은 하나의 값을 생성하기 위해 호출되는 constructor이다. 다른 데이터 타입 정의 방식으로는 조합형.. 더보기
지정된 횟수만큼 처리를 반복하기 온라인 저지 사이트의 문제들은 주어진 task를 수행하는 코드를 작성하게 한 다음, 지정된 포맷의 입력을 처리하게 한다. 이 때 입력은 전통적으로 다음과 같은 형식을 취하는 경우가 많다. 최초 입력되는 값은 정수값으로 몇 번 반복할 것인지를 결정한다. 이후 매 행마다 데이터를 입력하고, 이것을 1에서 입력한 값 만큼 제공한다. 일반적인 프로그래밍 언어에서는 이 과정이 별다른 테크닉을 요구하지 않는다. 기본적인 입력 방법과 반복문을 사용해서 처리하게 된다. 예를 들어 파이썬이라면 다음과 같이 코딩할 수 있겠다. n = int(input()) for _ in range(n): x = input() solve(x) 그런데 하스켈에서는 이를 어떻게 처리할 수 있을까? 간단한 문제가 하나 있다고 하자. 정수 하나.. 더보기
리스트의 홀수번째 원소만 골라내기 리스트의 자리로 필터링하는 문제이다. 원문 : https://www.hackerrank.com/challenges/fp-filter-positions-in-a-list/problem 접근 인덱스를 0부터 시작한다고 할 때, 홀수 인덱스에 있는 원소를 뺀 나머지 리스트를 구하는 함수를 작성해야 한다. 우선 원래 리스트에서 출발한다고 하면 head를 취해야 한다. 그리고 앞에서 두 개를 버린 리스트에 대해 재귀적으로 적용한다. f [] = [] f lst = (head . tail $ lst):(f . drop 2 $ lst) 간단한 문제인데, 이런 유형은 zip을 이용하는 것도 좋다. (파이썬에서는 보다 functional하기 풀기 위해서 zip을 잘 활용하면서 정작 함수형 언어에서 zip을 안 쓸 이유가.. 더보기
기본적인 필터링 이번 문제는 필터링에 관한 문제이다. 주어진 리스트와 X에 대해서 X보다 작은 값으로 리스트를 필터링한다. 문제 자체는 매우 간단하다. 원문: https://www.hackerrank.com/challenges/fp-filter-array/problem 스텁 문제에서 제공되는 스텁이다. main :: IO () main = do n [Int] 풀이 함수 f를 작성해야 한다. 함수 f는 정수와 정수의 리스트를 받아, 첫 인자보다 작은 정수만으로 필터링된 결과를 리턴한다. 이는 filter 함수 그자체의 사용이다. f n arr = filter (< n) arr 최종 main 함수를 풀어보면 readLn 함수로 n을 얻는다. getContents를 이용해서 inputData에 바인딩한다. inputData를.. 더보기
리스트의 각 원소를 복제하기 원문 : https://www.hackerrank.com/challenges/fp-list-replication/problem 이번 문제는 리스트 복제와 관련된 문제이다. replicate를 이용해서 특정한 타입의 값을 N회 반복하는 리스트로 만드는 것을 응용한다. 문제에서는 아래와 같은 스텁이 주어진다. main :: IO () main = getContents >>= mapM_ print . (\(n:arr) -> f n arr) . map read . words f n arr = -- implement this function 접근 일단 문제에 집중하자. 문제는 일련의 숫자가 (한 줄에 하나씩) 주어졌을 때, 맨 첫 숫자만큼 각 숫자를 반복한 리스트를 만드는 것이다. 편의상 한 칸에 하나의 숫자를 .. 더보기
헬로월드 X N 이번 문제는 hello world를 N번 찍는 문제이다. "간단한" 반복문 문제인데, 함수형 언어에서 반복문이라는게 있던가? 재귀적인 접근 함수형 언어에서는 반복문 대신에 함수의 재귀호출로 이를 대신한다. 따라서 다음과 같이 재귀호출을 이용한 함수를 생각해볼 수 있다. 이러한 패턴은 함수형 언어에서 반복문을 대체하는 가장 기본적인 패턴이다. sayHello :: Int -> IO () sayHello 0 = return () sayHello n = do putStrLn "Hello World" sayHello (n-1) 아래와 같이 확인해볼 수 있다. main :: IO () main = do n m a -> m [a]이다. Applicative 속성의 타입 a를 N번 반복해서 리스트로 만들어준다는 것인.. 더보기