E D R , A S I H C RSS

하스켈

Difference between r1.11 and the current

@@ -1,34 +1,97 @@
하스켈은 순수 함수형 프로그래밍 언어입니다. 사실 C++처럼 수행능력을 중시하는 언어를 선호하신다면 별로 권장하고 싶지는 않습니다. 수학에서 최소한의 공리만 가지고 겁나게 복잡한 것들을 수없이 정의하고 뇌가 꼬이는 공식을 만드는 있죠? 그런 느낌이 듭니다...
프로그래밍 언어는 결국 컴퓨터 상에서 실행되는 프로그램을 생성하기 위한 도구다. 가장 근본적이자 원시적인 언어는 기계어이고, 기계어의 거의 일대일 대응 격인 어셈블리어가 다음이다. 2학년 수업 중에 <컴퓨터 구조>에서 배우듯이 프로그램의 실행은 일련의 명령(instruction)을 클럭이 박동함에 따라 인출, 판독, 실행하는 과정이다. 즉 태생이 절차적(procedural)일 수 밖에 없다.

↓이거 코드 강조 어떻게 해요
-- 예제1: 피보나치
memo :: (Int -> a) -> Int -> a
memo f n = (map f [0..]) !! n
memo_fib = memo fib n where
fib 0 = 0
fib 1 = 1
fib n = memo fib (n-2) + memo fib (n-1)
어셈블리어에서는 프로그램에서 쓰이는 모든 데이터를 단순히 데이터 영역에 주루룩 나열한다면, 이제 C에서는 여러 데이터를 하나로 묶어 구조체로 만드는 구조적(structural) 프로그래밍을 지원한다. 그 구조체들 사이의 상하, 대응, 포함 관계나 상호작용하는 방법을 다루는 객체지향(object-oriented)이라는 개념이 등장하였고 C++, 자바 같은 주류 언어에서 문법적으로 지원하고 있다. 하지만 이들 언어로 작성한 코드는 다음과 같은 일반적인 개념이 성립한다.
 
1. 코드의 각각의 줄이 하나의 명령문(statement) 된다.
2. 번에 한 명령문을 실행하고 그 다음에 어떤 명령문을 실행할 지가 결정된다.

즉 프로그램의 원시적인 작동 방식이 여전히 코드 상에 반영되어 있는 것이다. 한 명령을 실행하고, 프로그램 카운터는 다음에 실행할 명령을 찾고, 그 명령문은 다음 클럭에 실행하고... 이걸 추상적인 수준으로 끌어올렸을 뿐 코드에 그 절차가 고스란히 묻어있는 것이다.

-- 예제2: 문자열 "n1 n2 n3 n4 ..." 내의 모든 숫자 더하기
total = foldr1 (+) (map read $ words usageList)
함수형(functional) 언어인 하스켈은 완전히 다른 방식으로 프로그램에 접근한다. 명령 인출-판독-실행은 잊어라. 흐름 제어도 잊어라. 문제를 푸는 데 필요한 정의들을 적어라. 그리고 그 정의에서 필요한 부분만 실행하라. 명령형(imperative) 언어에서는 추상적인 논리를 코드로 실행 가능한 절차로 표현한다면 하스켈에서는 코드로 논리를 그대로 표현하고 컴파일러가 그걸 알아서 실현 가능한 프로그램으로 만들어낸다. 하스켈 코드 줄은 '''기계에 종속적인''' 절차와 일대일 대응이 되지 않는다.
 
C 같은 언어로 프로그래밍을 처음 배울 때면 x = x+1 은 "x와 x+1가 같다"가 아니라 x에 x+1을 할당하는(assign) 거라고 배운다. 하스켈에서는 정말로 x = x+1 이라고 정의해버린다. (하지만 x를 실제로 쓰기 전에는 프로그램이 무한 루프에 빠지지는 않는데 이는 하스켈의 지연 평가(lazy evaluation)라는 특성과 관련이 있다. 자세한 건 검색)
 
또한 하스켈의 주요 특징 중 하나는 순수성인데, 함수 내에서 부수효과(side-effect)를 일으킬 수 없다는 것이다. 함수의 출력이 달라지는 요인은 오직 그 함수에 입력되는 인자 뿐이다. 인자가 같으면 무조건 출력도 같다. 어떤 함수 내에서 데이터에 가하는 어떤 수정도 다른 함수의 실행에 영향을 끼칠 수 없다. (그러기 위해서는 데이터를 항상 복사해야 하는데 자연스레 수행능력 면에서는 C++ 같은 언어보다 뒤쳐질 수 밖에 없다)
 
하스켈 코드가 읽기 힘든 것은 특유의 압축성 때문이다. 사실 풀어쓰면 그다지 어려울 것 없는 코드를, 단순한 연산들을 정체불명의 함수들(id, const, map, fold, zip, ($), (.), (>>=) 등)로 감싸고 한 줄에 모조리 때려박으면 이제 타 언어 사용자가 알아볼 수 없는 괴이한 코드가 된다. C나 자바가 구구단이라면 하스켈은 19단은 외워야 걸음마를 뗄 수 있는 것 같다.
 
'''예시 1'''
{{{
import Data.Bits
import Numeric
 
-- 32비트 정수를 8비트씩 잘라낸다
split :: Int -> [Int]
split n = [b1, b2, b3, b4]
where
b1 = n .&. 0xFF
b2 = (n `shiftR` 8) .&. 0xFF
b3 = (n `shiftR` 16) .&. 0xFF
b4 = (n `shiftR` 24) .&. 0xFF
 
main = print $ map showHex (split 0x12345678) -- ["12","34","56","78"] 출력
}}}
하스켈 코드를 본 적이 없어도 split 함수를 대충은 알아볼 수 있다. 이제 한 줄로 압축해보자.
{{{
split n = map (.&. 0xFF) (zipWith shiftR [n,n,n,n] [24,16..])
}}}
???????????????????????????????????????????????????????

'''예시 2''' - 리스트의 짝수 번째 원소들에만 2 곱하기
{{{
xs = [1, 10, 2, 20, 3, 30, 4, 40]
xs' = zipWith ($) (cycle [(*2), id]) xs -- 뭐라고요
main = print xs >> print xs'
 
-- 출력
[1,10,2,20,3,30,4,40]
[2,10,4,20,6,30,8,40]
}}}
 
'''예시 3''' - [https://algospot.com/judge/problem/read/LECTURE] 의 해답
{{{
import System.IO
import Data.List
import Control.Monad
 
-- 알아볼 수 없는 모나드의 향연
main = getLine >>= (\ n -> replicateM_ (read n :: Int) solve)
solve = getLine >>= (\ str -> putStrLn $ (concat . sort . pairup) str)
pairup str = sub [] str where
sub xs [] = xs
sub xs ys = sub ((2 `take` ys) : xs) (2 `drop` ys)
}}}
 
----

* 하스켈에 대한 전반적인 설명은 위키피디아 참조
* 한글: http://ko.wikipedia.org/wiki/%ED%95%98%EC%8A%A4%EC%BC%88
* 영어: http://en.wikipedia.org/wiki/Haskell_(programming_language)
* 한글: [http://ko.wikipedia.org/wiki/%ED%95%98%EC%8A%A4%EC%BC%88]
* 영어: [http://en.wikipedia.org/wiki/Haskell_(programming_language)]
*하스켈 공부 자료를 찾는다면 다음 사이트를 추천합니다. 여러 링크가 잘 정리되어 있습니다.
* https://sites.google.com/site/pieceofhaskell/
* [https://sites.google.com/site/pieceofhaskell/]
* 제로위키 내에는 하스켈 관련 기존 페이지가 2개 밖에 없네요..
* http://wiki.zeropage.org/wiki.php/BeginningHaskellLanguage
* http://wiki.zeropage.org/wiki.php/HaskellLanguage
* 영어 위키피디아의 [http://en.wikibooks.org/wiki/Haskell] 하스켈 위키책 번역하고 있습니다. - [이민석]
https://wikidocs.net/book/204
* [http://wiki.zeropage.org/wiki.php/BeginningHaskellLanguage]
* [http://wiki.zeropage.org/wiki.php/HaskellLanguage]
* 온라인 자료
* 위키피디아의 하스켈 위키책 [http://en.wikibooks.org/wiki/Haskell] 
* 참고로 위키책은 제가 번역하고 있어요.. [https://wikidocs.net/book/204] - [이민석]
* Real World Haskell [http://book.realworldhaskell.org/]
* 위키책의 코드는 문법 설명을 위한 인위적인 예제들
* 여러 라이브러리를 활용하는 법에 대한 실전 코드 제공(파일 I/O, 네트워크, GUI, 데이터베이스...)
* 각 분야에 대한 사전 지식은 필요
* 파일 I/O: 파일 핸들, 읽기/쓰기 모드, 파일 및 디렉토리 체계
* 데이터베이스: 쿼리 구문, 데이터베이의 구조
* 사실 실전 경험이 없다면 예제 코드를 읽기가 상당히 벅찰 것
* 하지만 있다면! 코드만 읽어도 라이브러리 사용법을 대강 파악할 수 있음
* 관련 지식을 일일이 설명했으면 책이 너무 길어졌을 것. 분량 면에서 적당한 벌충안
* Parallel and Concurrent Programming in Haskell [http://chimera.labs.oreilly.com/books/1230000000929/index.html]
* 스탠포드 대학 강의노트(CS240h: Functional Systems in Haskell) [http://www.scs.stanford.edu/11au-cs240h/]
* What I Wish I Knew When Learning Haskell 2.1 [http://dev.stephendiehl.com/hask/]

--
----

* 한글로 된 하스켈 책이 두 개 있는 것 같은데 혹시 둘 다 별로였나? - [서민관]
- [http://www.yes24.com/24/goods/3479645?scode=032&OzSrank=2],
- [http://www.yes24.com/24/goods/12155304?scode=032&OzSrank=1]
- 하스켈 공부를 군대에서 시작해서 볼 수가 없었어요.. 예전에 학교 도서관에서 둘 중 하나를 본 적은 있습니다. 무엇보다 http://book.realworldhaskell.org/ 이게 내용이 엄청 좋습니다. 위키책은 솔직히 문법 배우는 것 이상의 의미는 없고 실용 서적으로는 최고인 것 같네요. - 이민석
- 하스켈 공부를 군대에서 시작해서 볼 수가 없었어요.. 예전에 학교 도서관에서 둘 중 하나를 본 적은 있습니다. 무엇보다 http://book.realworldhaskell.org/ 이게 내용이 좋습니다. 위키책은 솔직히 문법 배우는 것 이상의 의미는 없고 실용 서적으로는 최고인 것 같네요. - 이민석
* 첫 번째는 2번씩이나 읽었지만 설명이 친절했다고 말하기 힘드네요. :( - 이원준


프로그래밍 언어는 결국 컴퓨터 상에서 실행되는 프로그램을 생성하기 위한 도구다. 가장 근본적이자 원시적인 언어는 기계어이고, 기계어의 거의 일대일 대응 격인 어셈블리어가 그 다음이다. 2학년 수업 중에 <컴퓨터 구조>에서 배우듯이 프로그램의 실행은 일련의 명령(instruction)을 클럭이 박동함에 따라 인출, 판독, 실행하는 과정이다. 즉 태생이 절차적(procedural)일 수 밖에 없다.

어셈블리어에서는 프로그램에서 쓰이는 모든 데이터를 단순히 데이터 영역에 주루룩 나열한다면, 이제 C에서는 여러 데이터를 하나로 묶어 구조체로 만드는 구조적(structural) 프로그래밍을 지원한다. 그 구조체들 사이의 상하, 대응, 포함 관계나 상호작용하는 방법을 다루는 객체지향(object-oriented)이라는 개념이 등장하였고 C++, 자바 같은 주류 언어에서 문법적으로 지원하고 있다. 하지만 이들 언어로 작성한 코드는 다음과 같은 일반적인 개념이 성립한다.

1. 코드의 각각의 줄이 하나의 명령문(statement)이 된다.
2. 한 번에 한 명령문을 실행하고 그 다음에 어떤 명령문을 실행할 지가 결정된다.

즉 프로그램의 원시적인 작동 방식이 여전히 코드 상에 반영되어 있는 것이다. 한 명령을 실행하고, 프로그램 카운터는 다음에 실행할 명령을 찾고, 그 명령문은 다음 클럭에 실행하고... 이걸 추상적인 수준으로 끌어올렸을 뿐 코드에 그 절차가 고스란히 묻어있는 것이다.

함수형(functional) 언어인 하스켈은 완전히 다른 방식으로 프로그램에 접근한다. 명령 인출-판독-실행은 잊어라. 흐름 제어도 잊어라. 문제를 푸는 데 필요한 정의들을 적어라. 그리고 그 정의에서 필요한 부분만 실행하라. 명령형(imperative) 언어에서는 추상적인 논리를 코드로 실행 가능한 절차로 표현한다면 하스켈에서는 코드로 논리를 그대로 표현하고 컴파일러가 그걸 알아서 실현 가능한 프로그램으로 만들어낸다. 하스켈 코드 한 줄은 기계에 종속적인 절차와 일대일 대응이 되지 않는다.

C 같은 언어로 프로그래밍을 처음 배울 때면 x = x+1 은 "x와 x+1가 같다"가 아니라 x에 x+1을 할당하는(assign) 거라고 배운다. 하스켈에서는 정말로 x = x+1 이라고 정의해버린다. (하지만 x를 실제로 쓰기 전에는 프로그램이 무한 루프에 빠지지는 않는데 이는 하스켈의 지연 평가(lazy evaluation)라는 특성과 관련이 있다. 자세한 건 검색)

또한 하스켈의 주요 특징 중 하나는 순수성인데, 함수 내에서 부수효과(side-effect)를 일으킬 수 없다는 것이다. 함수의 출력이 달라지는 요인은 오직 그 함수에 입력되는 인자 뿐이다. 인자가 같으면 무조건 출력도 같다. 어떤 함수 내에서 데이터에 가하는 어떤 수정도 다른 함수의 실행에 영향을 끼칠 수 없다. (그러기 위해서는 데이터를 항상 복사해야 하는데 자연스레 수행능력 면에서는 C++ 같은 언어보다 뒤쳐질 수 밖에 없다)

하스켈 코드가 읽기 힘든 것은 특유의 압축성 때문이다. 사실 풀어쓰면 그다지 어려울 것 없는 코드를, 단순한 연산들을 정체불명의 함수들(id, const, map, fold, zip, ($), (.), (>>=) 등)로 감싸고 한 줄에 모조리 때려박으면 이제 타 언어 사용자가 알아볼 수 없는 괴이한 코드가 된다. C나 자바가 구구단이라면 하스켈은 19단은 외워야 걸음마를 뗄 수 있는 것 같다.

예시 1
import Data.Bits
import Numeric

-- 32비트 정수를 8비트씩 잘라낸다
split :: Int -> [Int]
split n = [b1, b2, b3, b4]
  where
    b1 = n .&. 0xFF
    b2 = (n `shiftR` 8) .&. 0xFF
    b3 = (n `shiftR` 16) .&. 0xFF
    b4 = (n `shiftR` 24) .&. 0xFF

main = print $ map showHex (split 0x12345678) -- ["12","34","56","78"] 출력
하스켈 코드를 본 적이 없어도 split 함수를 대충은 알아볼 수 있다. 이제 한 줄로 압축해보자.
split n = map (.&. 0xFF) (zipWith shiftR [n,n,n,n] [24,16..])
???????????????????????????????????????????????????????

예시 2 - 리스트의 짝수 번째 원소들에만 2 곱하기
xs = [1, 10, 2, 20, 3, 30, 4, 40]
xs' = zipWith ($) (cycle [(*2), id]) xs -- 뭐라고요
main = print xs >> print xs'

-- 출력
[1,10,2,20,3,30,4,40]
[2,10,4,20,6,30,8,40]

예시 3 - https://algospot.com/judge/problem/read/LECTURE 의 해답
import System.IO
import Data.List
import Control.Monad

-- 알아볼 수 없는 모나드의 향연
main = getLine >>= (\ n -> replicateM_ (read n :: Int) solve)
solve = getLine >>= (\ str -> putStrLn $ (concat . sort . pairup) str)
pairup str = sub [] str where
    sub xs [] = xs
    sub xs ys = sub ((2 `take` ys) : xs) (2 `drop` ys)





Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:31:30
Processing time 0.1034 sec