1
0
mirror of https://github.com/adambard/learnxinyminutes-docs.git synced 2025-08-30 18:10:20 +02:00

Simplify language codes in directories

This commit is contained in:
Boris Verkhovskiy
2024-12-08 20:29:09 -07:00
parent 3da692272f
commit 912da583da
489 changed files with 0 additions and 0 deletions

381
ko/bash.md Normal file
View File

@@ -0,0 +1,381 @@
---
language: Bash
contributors:
- ["Max Yankov", "https://github.com/golergka"]
- ["Darren Lin", "https://github.com/CogBear"]
- ["Alexandre Medeiros", "http://alemedeiros.sdf.org"]
- ["Denis Arh", "https://github.com/darh"]
- ["akirahirose", "https://twitter.com/akirahirose"]
- ["Anton Strömkvist", "http://lutic.org/"]
- ["Rahil Momin", "https://github.com/iamrahil"]
- ["Gregrory Kielian", "https://github.com/gskielian"]
- ["Etan Reisner", "https://github.com/deryni"]
- ["Jonathan Wang", "https://github.com/Jonathansw"]
- ["Leo Rudberg", "https://github.com/LOZORD"]
- ["Betsy Lorton", "https://github.com/schbetsy"]
- ["John Detter", "https://github.com/jdetter"]
translators:
- ["Wooseop Kim", "https://github.com/linterpreteur"]
filename: LearnBash-kr.sh
lang: ko-kr
---
Bash는 유닉스 셸의 이름이며, 리눅스와 맥 OS X의 기본 셸로 그리고 GNU 운영체제를 위한 셸로서 배포되었습니다.
이하의 거의 모든 예시들은 셸 스크립트의 일부이거나 셸에서 바로 실행할 수 있습니다.
[(영어) 이곳에서 더 알아보세요.](http://www.gnu.org/software/bash/manual/bashref.html)
```bash
#!/bin/bash
# 스크립트의 첫 줄은 시스템에게 스크립트의 실행법을 알려주는 '셔뱅'입니다.
# https://ko.wikipedia.org/wiki/%EC%85%94%EB%B1%85
# 이미 보았듯이 주석은 #으로 시작합니다. 셔뱅 또한 주석입니다.
# 간단한 헬로 월드
echo 헬로 월드!
# 각각의 명령어는 개행 혹은 세미콜론 이후에 시작됩니다.
echo '첫번째 줄'; echo '두번째 줄'
# 변수 선언은 다음과 같습니다.
Variable="어떤 문자열"
# 하지만 다음은 틀린 형태입니다.
Variable = "어떤 문자열"
# Bash는 Variable이 실행해야 하는 명령어라고 판단할 것이고, 해당 명령어를 찾을
# 수 없기 때문에 에러를 발생시킬 것입니다.
# 다음도 같습니다.
Variable= '어떤 문자열'
# Bash는 '어떤 문자열'이 실행해야 하는 명령어라고 판단하여 에러를 발생시킬 것입니다.
# (이 경우에 'Variable=' 부분은 '어떤 문자열' 명령어의 스코프에서만 유효한
# 변수 할당으로 해석됩니다.)
# 변수 사용은 다음과 같습니다.
echo $Variable
echo "$Variable"
echo '$Variable'
# 할당, 내보내기 등 변수 자체를 사용할 때에는 $ 없이 이름을 적습니다.
# 변수의 값을 사용할 때에는 $를 사용해야 합니다.
# 작은 따옴표는 변수를 확장시키지 않는다는 사실에 주의하세요.
# (역자 주: '$Variable'은 변수 Variable의 값이 아닌 문자열 "$Variable"입니다.)
# 인수 확장은 ${ }입니다.
echo ${Variable}
# 이는 인수 확장의 간단한 예시입니다.
# 인수 확장은 변수로부터 값을 받아 그 값을 "확장"하거나 출력합니다.
# 확장을 통해 인수나 그 값이 변경될 수 있습니다.
# 이하는 확장에 대한 다른 예시들입니다.
# 변수에서의 문자열 치환
echo ${Variable/Some/A}
# 처음으로 나타나는 "Some"를 "A"로 치환합니다.
# 변수의 부분열
Length=7
echo ${Variable:0:Length}
# 변수 값에서 처음 7개 문자만을 반환합니다.
# 변수의 기본값
echo ${Foo:-"Foo가_없거나_비어_있을_때의_기본값"}
# null(Foo=) 값이나 빈 문자열(Foo="")일 경우에만 작동합니다. 0은 (Foo=0)은 0입니다.
# 기본값을 반환할 뿐 변수 값을 변경하지는 않는다는 사실에 주목하세요.
# 중괄호 확장 { }
# 임의의 문자열을 생성합니다.
echo {1..10}
echo {a..z}
# 시작 값으로부터 끝 값까지의 범위를 출력합니다.
# 내장 변수
# 유용한 내장 변수들이 있습니다.
echo "마지막 프로그램의 반환값: $?"
echo "스크립트의 PID: $$"
echo "스크립트에 넘겨진 인자의 개수: $#"
echo "스크립트에 넘겨진 모든 인자: $@"
echo "각각 변수로 쪼개진 스크립트 인자: $1 $2..."
# echo와 변수의 사용법을 알게 되었으니,
# bash의 기초를 조금 더 배워봅시다!
# 현재 디렉토리는 `pwd` 명령어로 알 수 있습니다.
# `pwd`는 "print working directory(작업 디렉토리 출력)"의 약자입니다.
# 내장 변수`$PWD`를 사용할 수도 있습니다.
# 이하는 모두 동일합니다.
echo "I'm in $(pwd)" # `pwd`를 실행하여 문자열에 보간
echo "I'm in $PWD" # 변수를 보간
# 터미널이나 결과의 출력물이 너무 많다면
# 명령어 `clear`를 이용해 화면을 지울 수 있습니다.
clear
# 컨트롤+L 또한 화면을 지울 수 있습니다.
# 입력 값 읽기
echo "이름이 뭐에요?"
read Name # 변수 선언이 필요 없다는 데 주목하세요.
echo $Name님, 안녕하세요!
# 평범한 if 구조도 있습니다.
# 'man test'로 조건문에 대해 더 알아보세요.
if [ $Name != $USER ]
then
echo "사용자가 아닙니다."
else
echo "사용자입니다."
fi
# $Name이 비어 있다면, bash는 위의 조건을 다음과 같이 인식합니다.
if [ != $USER ]
# 이는 문법적으로 유효하지 않습니다.
# 따라서 bash에서 비어 있을 수 있는 변수를 "안전하게" 사용하는 법은 다음과 같습니다.
if [ "$Name" != $USER ] ...
# $Name이 비어 있다면 bash는
if [ "" != $USER ] ...
# 와 같이 인식하여 예상한 대로 동작합니다.
# 조건부 실행도 있습니다.
echo "항상 실행" || echo "첫 명령어가 실패해야 실행"
echo "항상 실행" && echo "첫 명령어가 실패하지 않아야 실행"
# if문과 함께 &&와 ||을 사용하려면, 대괄호가 여러 쌍 필요합니다.
if [ "$Name" == "철수" ] && [ "$Age" -eq 15 ]
then
echo "$Name이 철수이고 $Age가 15일 때 실행"
fi
if [ "$Name" == "민희" ] || [ "$Name" == "상민" ]
then
echo "$Name이 민희이거나 상민일 때 실행"
fi
# 표현식은 다음 형식으로 표기됩니다.
echo $(( 10 + 5 ))
# 다른 프로그래밍 언어와는 달리, bash는 셸이기 때문에 현재 디렉토리의 컨텍스트에서
# 실행됩니다. 현재 디렉토리의 파일과 디렉토리를 ls 명령어로 나열할 수 있습니다.
ls
# 다음은 실행을 제어하는 옵션의 예시입니다.
ls -l # 모든 파일과 디렉토리를 분리된 줄에 나열
ls -t # 디렉토리 내용을 마지막으로 수정된 날짜(내림차순)에 따라 정렬
ls -R # 이 디렉토리와 그 안의 모든 디렉토리에 대해 재귀적으로 `ls` 실행
# 이전 명령어의 결과는 다음 명령어에 입력될 수 있습니다.
# grep 명령어는 입력을 주어진 패턴에 따라 필터링합니다. 다음은 현재 디렉토리의
# .txt 파일을 나열하는 방법입니다.
ls -l | grep "\.txt"
# `cat`을 이용해 stdout으로 파일을 출력합니다.
cat file.txt
# `cat`으로 파일을 읽을 수도 있습니다.
Contents=$(cat file.txt)
echo "파일 시작\n$Contents\n파일 끝"
# `cp`를 이용해 파일이나 디렉토리를 다른 곳으로 복사할 수 있습니다.
# `cp`는 원본의 새로운 버전을 생성하므로 사본을 편집하는 것은
# 원본에 영향을 주지 않으며 그 반대도 마찬가지입니다.
# 목표 위치에 이미 파일이 있다면 덮어쓰게 됩니다.
cp srcFile.txt clone.txt
cp -r srcDirectory/ dst/ # 재귀적으로 복사
# 컴퓨터 간에 파일을 공유하려고 한다면 `scp` 혹은 `sftp`를 사용합니다.
# `scp`는 `cp`와 매우 유사하게 동작하며
# `sftp`는 더 상호작용적입니다.
# `mv`로 파일 혹은 디렉토리를 다른 곳으로 이동합니다.
# `mv`는 `cp`와 유사하지만 원본을 삭제합니다.
# 또한 `mv`로 파일의 이름을 바꿀 수도 있습니다.
mv s0urc3.txt dst.txt # sorry, l33t hackers...
# bash는 현재 디렉토리의 컨텍스트에서 실행되기 때문에, 다른 디렉토리에서 명령어를
# 실행하고 싶으실 수 있습니다. cd를 이용해 위치를 변경합니다.
cd ~ # 홈 디렉토리로 변경
cd .. # 한 디렉토리 위로 이동
# (즉 /home/username/Downloads에서 /home/username로)
cd /home/username/Documents # 특정 디렉토리로 이동
cd ~/Documents/.. # 아직도 홈 디렉토리... 아닌가??
# 서브셸로 디렉토리를 넘어서 작업할 수도 있습니다.
(echo "처음엔 여기 $PWD") && (cd 어딘가; echo "이제는 여기 $PWD")
pwd # 아직도 첫 디렉토리에 있음
# `mkdir`로 새 디렉토리를 만듭니다.
mkdir myNewDir
# `-p` 플래그는 필요하다면 해당 디렉토리의 경로 중간에 있는 디렉토리를 생성합니다.
mkdir -p myNewDir/with/intermediate/directories
# (stdin, stdout, stderr로) 명령어의 입출력을 리디렉션할 수 있습니다.
# stdin의 내용을 ^EOF$까지 읽고 hello.py에 그 내용을 덮어씁니다.
cat > hello.py << EOF
#!/usr/bin/env python
from __future__ import print_function
import sys
print("#stdout", file=sys.stdout)
print("#stderr", file=sys.stderr)
for line in sys.stdin:
print(line, file=sys.stdout)
EOF
# stdin, stdoutk, stderr을 다양한 방법으로 리디렉션하여 hello.py를 실행합니다.
python hello.py < "input.in"
python hello.py > "output.out"
python hello.py 2> "error.err"
python hello.py > "output-and-error.log" 2>&1
python hello.py > /dev/null 2>&1
# 출력 오류는 이미 파일이 있을 경우 덮어쓰지만,
# 덮어쓰는 대신에 내용에 추가하고 싶다면 ">>"를 사용합니다.
python hello.py >> "output.out" 2>> "error.err"
# output.out에 덮어쓰고, error.err에 추가하고, 줄을 세기
info bash 'Basic Shell Features' 'Redirections' > output.out 2>> error.err
wc -l output.out error.err
# 명령어를 실행하고 그 파일 디스크립터를 출력 (예: /dev/fd/123)
# man fd 참고
echo <(echo "#helloworld")
# output.out을 "#helloworld"으로 덮어쓰기
cat > output.out <(echo "#helloworld")
echo "#helloworld" > output.out
echo "#helloworld" | cat > output.out
echo "#helloworld" | tee output.out >/dev/null
# 임시 파일을 지울 수 있습니다. ('-i'로 대화식 실행)
# 경고: `rm` 명령어는 되돌릴 수 없습니다.
rm -v output.out error.err output-and-error.log
rm -r tempDir/ # 재귀적으로 삭제
# 다른 명령어에서 $()을 이용해 명령어를 치환할 수도 있습니다.
# 다음 명령어는 현재 디렉토리의 파일 및 디렉토리의 수를 표시합니다.
echo "$(ls | wc -l)개 항목이 있습니다."
# 백틱(``)을 이용할 수도 있지만 이 방식을 이용하면 중첩할 수 없기 때문에
# $()을 사용하는 것이 더 좋습니다.
echo "`ls | wc -l`개 항목이 있습니다."
# 자바나 C++의 switch와 비슷하게 동작하는 case 문을 사용할 수 있습니다.
case "$Variable" in
# 충족시킬 조건을 나열
0) echo "0입니다.";;
1) echo "1입니다.";;
*) echo "널이 아닌 값입니다.";;
esac
# for 반복문은 주어진 인자만큼 반복합니다.
# 다음은 $Variable을 세 번 출력합니다.
for Variable in {1..3}
do
echo "$Variable"
done
# 혹은 "전통적인 for 반복문" 방식을 쓸 수도 있습니다.
for ((a=1; a <= 3; a++))
do
echo $a
done
# 파일에도 적용될 수 있습니다.
# 다음은 file1과 file2에 'cat' 명령어를 실행합니다.
for Variable in file1 file2
do
cat "$Variable"
done
# 혹은 명령어의 결과에도 이용할 수 있습니다.
# 다음은 ls의 결과를 cat합니다.
for Output in $(ls)
do
cat "$Output"
done
# while 반복문
while [ true ]
do
echo "반복문 몸체"
break
done
# 함수를 정의할 수도 있습니다.
# 정의:
function foo ()
{
echo "인자는 함수 인자처럼 작동합니다. $@"
echo "그리고 $1 $2..."
echo "함수입니다."
return 0
}
# 혹은 단순하게
bar ()
{
echo "함수를 선언하는 다른 방법"
return 0
}
# 함수 호출
foo "My name is" $Name
# 몇 가지 유용한 명령어를 알아두면 좋습니다.
# file.txt의 마지막 10줄 출력
tail -n 10 file.txt
# file.txt의 첫 10줄 출력
head -n 10 file.txt
# file.txt 줄 별로 정렬
sort file.txt
# 중복되는 줄을 생략하거나 -d를 이용하여 보고
uniq -d file.txt
# ',' 문자 이전의 첫 열만 출력
cut -d ',' -f 1 file.txt
# file.txt에서 'okay'를 모두 'great'로 교체 (정규식 호환)
sed -i 's/okay/great/g' file.txt
# file.txt에서 정규식에 맞는 모든 줄을 stdin에 출력
# 다음 예시는 "foo"로 시작해 "bar"로 끝나는 줄 출력
grep "^foo.*bar$" file.txt
# "-c" 옵션을 넘겨 줄 번호를 대신 출력
grep -c "^foo.*bar$" file.txt
# 다른 유용한 옵션
grep -r "^foo.*bar$" someDir/ # 재귀적으로 `grep`
grep -n "^foo.*bar$" file.txt # 줄 번호 매기기
grep -rI "^foo.*bar$" someDir/ # 재귀적으로 `grep`하되 바이너리 파일은 무시
# 같은 검색으로 시작하여 "baz"를 포함하는 줄만 필터
grep "^foo.*bar$" file.txt | grep -v "baz"
# 정규식이 아니라 문자열로 검색하고 싶다면
# fgrep 혹은 grep -F
fgrep "foobar" file.txt
# trap 명령어로 스크립트에서 신호를 받을 때 명령어를 실행할 수 있습니다.
# 다음 명령어는 셋 중 한 가지 신호를 받으면 rm 명령어를 실행합니다.
trap "rm $TEMP_FILE; exit" SIGHUP SIGINT SIGTERM
# `sudo`를 통해 슈퍼이용자로 명령어를 실행합니다.
NAME1=$(whoami)
NAME2=$(sudo whoami)
echo "$NAME1였다가 더 강한 $NAME2가 되었다"
# 'help' 명령어로 내장 문서를 읽을 수 있습니다.
help
help help
help for
help return
help source
help .
# man으로 매뉴얼을 읽을 수도 있습니다.
apropos bash
man 1 bash
man bash
# info 명령어로 문서를 읽습니다. (?로 도움말)
apropos info | grep '^info.*('
man info
info info
info 5 info
# bash의 info 문서를 읽어 보세요.
info bash
info bash 'Bash Features'
info bash 6
info --apropos bash
```

85
ko/bf.md Normal file
View File

@@ -0,0 +1,85 @@
---
language: BF
filename: learnbf-kr.bf
contributors:
- ["Prajit Ramachandran", "http://prajitr.github.io/"]
- ["Mathias Bynens", "http://mathiasbynens.be/"]
translators:
- ["JongChan Choi", "http://0xABCDEF.com/"]
- ["Peter Lee", "http://peterjlee.com/"]
lang: ko-kr
---
Brainfuck(문장을 시작하는 단어가 아닌이상 첫글자는 대문자를 사용하지 않습니다)은
여덟가지 명령어만으로 튜링-완전한 최소주의 프로그래밍 언어입니다.
```bf
"><+-.,[]" 이외의 문자들은 무시됩니다. (쌍따옴표는 제외)
브레인퍽은 30,000 칸 짜리의 0으로 초기화된 배열과,
현재 칸을 가르키는 포인터로 표현됩니다.
여덟가지의 명령어는 다음과 같습니다:
+ : 포인터가 가르키는 현재 칸의 값을 1 증가시킵니다.
- : 포인터가 가르키는 현재 칸의 값을 1 감소시킵니다.
> : 포인터가 다음 칸(오른쪽 칸)을 가르키도록 이동시킵니다.
< : 포인터가 이전 칸(왼쪽 칸)을 가르키도록 이동시킵니다.
. : 현재 칸의 값을 ASCII 문자로 출력합니다. (즉, 65 = 'A')
, : 하나의 문자를 입력받고 그 값을 현재 칸에 대입합니다.
[ : 현재 칸의 값이 0이면 짝이 맞는 ] 명령으로 넘어갑니다.
0이 아니면 다음 명령어로 넘어갑니다.
] : 현재 칸의 값이 0이면 다음 명령어로 넘어갑니다.
0이 아니면 짝이 맞는 [ 명령으로 다시 돌아갑니다.
[이랑 ]은 while 루프를 만들어냅니다. 무조건, 짝이 맞아야 합니다.
몇가지 간단한 브레인퍽 프로그램을 보겠습니다.
++++++ [ > ++++++++++ < - ] > +++++ .
이 프로그램은 문자 'A'를 출력합니다. 처음에는, 반복할 횟수를 정하기 위한 값을
만들기 위해 첫번째 칸의 값을 6으로 증가시킵니다. 그리고 루프로 들어가서([)
두번째 칸으로 넘어갑니다. 루프 안에서는 두번째 칸의 값을 10 증가시키고,
다시 첫번째 칸으로 넘어가서 값을 1 감소시킵니다. 이 루프는 여섯번 돕니다.
(첫번째 칸의 값을 6번 감소시켜서 0이 될 때 까지는 ] 명령을 만날 때마다
루프의 시작 지점으로 돌아갑니다)
이 시점에서, 두번째 칸의 값은 60이고, 포인터는 값이 0인 첫번째 칸에 위치합니다.
여기서 두번째 칸으로 넘어간 다음 값을 5 증가시키면 두번째 칸의 값이 65가 되고,
65는 문자 'A'에 대응하는 아스키 코드이기 때문에, 두번째 칸의 값을 출력하면
터미널에 'A'가 출력됩니다.
, [ > + < - ] > .
이 프로그램은 사용자로부터 문자 하나를 입력받아 첫번째 칸에 집어넣습니다.
그리고 루프에 들어가서, 두번째 칸으로 넘어가 값을 한 번 증가시킨 다음,
다시 첫번째 칸으로 넘어가서 값을 한 번 감소시킵니다.
이는 첫번째 칸의 값이 0이 될 때까지 지속되며,
두번째 칸은 첫번째 칸이 갖고있던 값을 가지게 됩니다.
루프가 종료되면 포인터는 첫번째 칸을 가르키기 때문에 두번째 칸으로 넘어가고,
해당 아스키 코드에 대응하는 문자를 출력합니다.
또한 공백문자는 순전히 가독성을 위해서 작성되었다는 것을 기억하세요.
다음과 같이 작성해도 똑같이 돌아갑니다:
,[>+<-]>.
한 번 돌려보고 아래의 프로그램이 실제로 무슨 일을 하는지 맞춰보세요:
,>,< [ > [ >+ >+ << -] >> [- << + >>] <<< -] >>
이 프로그램은 두 개의 숫자를 입력받은 뒤, 그 둘을 곱합니다.
위 코드는 일단 두 번의 입력을 받고, 첫번째 칸의 값만큼 바깥 루프를 돕니다.
그리고 루프 안에서 다시 두번째 칸의 값만큼 안쪽의 루프를 돕니다.
그리고 그 루프에서는 세번째 칸의 값을 증가시키는데, 문제가 하나 있습니다:
내부 루프가 한 번 끝나게 되면 두번째 칸의 값은 0이 됩니다.
그럼 다시 바깥 루프를 돌 때에 안쪽의 루프를 돌지 않게 되는데, 이를 해결하려면
네번째 칸의 값도 같이 증가시킨 다음, 그 값을 두번째 칸으로 옮기면 됩니다.
그러면 세번째 칸에 곱셈의 결과가 남습니다.
```
여기까지 브레인퍽이었습니다. 참 쉽죠?
재미삼아 브레인퍽 프로그램이나 다른 언어로 브레인퍽 인터프리터를 작성해보세요.
인터프리터 구현은 간단한 편인데,
사서 고생하는 것을 즐기는 편이라면 한 번 작성해보세요… 브레인퍽으로.

147
ko/clojure-macros.md Normal file
View File

@@ -0,0 +1,147 @@
---
language: Clojure macros
filename: learnclojuremacros-kr.clj
contributors:
- ["Adam Bard", "http://adambard.com/"]
translators:
- ["Eunpyoung Kim", "https://github.com/netpyoung"]
lang: ko-kr
---
다른 모든 Lisp와 마찬가지로, Clojure가 가진 [동형성(homoiconicity)](https://en.wikipedia.org/wiki/Homoiconic)은
"매크로"라고 불리는 코드 생성 루틴을 작성할 수 있도록 언어의 전체적인 범위에 접근할 수 있게 해줍니다.
매크로는 필요에 맞게 언어를 바꿀 수 있는 강력한 방법을 제공합니다.
주의하시기 바랍니다. 함수로도 충분히 해결할 수 있는 문제를 매크로로 작성하게 된다면, 좋은 코드라고 할 수 없습니다.
인자가 평가되는 시점을 제어해야 할 때만 매크로를 사용하는게 좋습니다.
Clojure랑 친해지면 쉽게 따라갈 수 있습니다. [Clojure in Y Minutes](/docs/ko-kr/clojure-kr/)를 한번 읽어보세요.
```clojure
;; defmacro로 매크로를 정의합니다.
;; 매크로는 clojure 코드로 평가될 수 있는 리스트를 반환해야 합니다.
;;
;; 다음 매크로는 (reverse "Hello World") 라고 쓴 것과 같습니다.
(defmacro my-first-macro []
(list reverse "Hello World"))
;; macroexpand나 macroexpand-1을 사용해서 매크로의 결과를 확인할 수 있습니다.
;;
;; 호출하는 부분이 '(quote)된 것을 주목합니다.
(macroexpand '(my-first-macro))
;; -> (#<core$reverse clojure.core$reverse@xxxxxxxx> "Hello World")
;; macroexpand의 결과를 바로 평가할 수 있습니다.
(eval (macroexpand '(my-first-macro)))
; -> (\d \l \o \r \W \space \o \l \l \e \H)
;; 하지만, 함수와 같이 좀 더 간결한 구문을 이용하는게 좋습니다:
(my-first-macro) ; -> (\d \l \o \r \W \space \o \l \l \e \H)
;; 더 간결한 quote 구문 ( ' )을 이용하여, 보다 편리하게 매크로 안에서 리스트를 만들 수 있습니다:
(defmacro my-first-quoted-macro []
'(reverse "Hello World"))
(macroexpand '(my-first-quoted-macro))
;; -> (reverse "Hello World")
;; reverse는 더 이상 함수 객체가 아니라 심볼이라는 것에 주목하세요.
;; 매크로는 인자를 받을 수 있습니다.
(defmacro inc2 [arg]
(list + 2 arg))
(inc2 2) ; -> 4
;; 하지만, quote된 리스트를 사용하면 에러가 발생합니다.
;; 인자도 quote되기 때문입니다.
;; 이를 해결하기 위해, clojure는 매크로를 quote할 수 있는 방법을 제공합니다: `.
;; ` 안에서 ~를 사용하면 외부 스코프에 접근할 수 있습니다.
(defmacro inc2-quoted [arg]
`(+ 2 ~arg))
(inc2-quoted 2)
;; destructuring args도 사용할 수 있습니다. ~@를 사용하여 리스트 변수를 확장할 수 있습니다.
(defmacro unless [arg & body]
`(if (not ~arg)
(do ~@body))) ; do를 빼먹지 마세요!
(macroexpand '(unless true (reverse "Hello World")))
;; ->
;; (if (clojure.core/not true) (do (reverse "Hello World")))
;; (unless)는 첫 번째 인자가 false일 때, body를 평가하고 반환합니다.
;; 그렇지않으면, nil을 반환합니다.
(unless true "Hello") ; -> nil
(unless false "Hello") ; -> "Hello"
;; 주의하지 않으면, 매크로는 변수를 덮어쓰는 등 큰 문제를 일으킬 수 있습니다.
(defmacro define-x []
'(do
(def x 2)
(list x)))
(def x 4)
(define-x) ; -> (2)
(list x) ; -> (2)
;; 이를 피하기 위해, gensym을 이용하여 고유한 식별자를 얻을 수 있습니다.
(gensym 'x) ; -> x1281 (혹은 다른 식별자)
(defmacro define-x-safely []
(let [sym (gensym 'x)]
`(do
(def ~sym 2)
(list ~sym))))
(def x 4)
(define-x-safely) ; -> (2)
(list x) ; -> (4)
;; ` 안에서 #를 사용하면 자동으로 각 심볼에 대한 gensym을 생성할 수 있습니다.
(defmacro define-x-hygienically []
`(do
(def x# 2)
(list x#)))
(def x 4)
(define-x-hygienically) ; -> (2)
(list x) ; -> (4)
;; 매크로를 만들 때는 보통 헬퍼 함수를 많이 이용합니다.
;; 인라인 산술 문법을 지원하는 몇 개의 헬퍼 함수를 만들어 봅시다.
(declare inline-2-helper)
(defn clean-arg [arg]
(if (seq? arg)
(inline-2-helper arg)
arg))
(defn apply-arg
"Given args [x (+ y)], return (+ x y)"
[val [op arg]]
(list op val (clean-arg arg)))
(defn inline-2-helper
[[arg1 & ops-and-args]]
(let [ops (partition 2 ops-and-args)]
(reduce apply-arg (clean-arg arg1) ops)))
;; 매크로를 만들지 않고, 바로 테스트해볼 수 있습니다.
(inline-2-helper '(a + (b - 2) - (c * 5))) ; -> (- (+ a (- b 2)) (* c 5))
; 하지만, 이 함수를 컴파일 타임에 실행하려면 매크로로 만들어야 합니다.
(defmacro inline-2 [form]
(inline-2-helper form))
(macroexpand '(inline-2 (1 + (3 / 2) - (1 / 2) + 1)))
; -> (+ (- (+ 1 (/ 3 2)) (/ 1 2)) 1)
(inline-2 (1 + (3 / 2) - (1 / 2) + 1))
; -> 3 (실제로는 3N이라는 결과가 나옵니다. / 연산자를 사용하면 숫자가 유리수로 캐스팅되기 때문입니다.)
```
### 더 읽어볼거리
- [Writing Macros](http://www.braveclojure.com/writing-macros/)
- [Official docs](http://clojure.org/macros)
- [When to use macros?](https://lispcast.com/when-to-use-a-macro/)

383
ko/clojure.md Normal file
View File

@@ -0,0 +1,383 @@
---
language: Clojure
filename: learnclojure-kr.clj
contributors:
- ["Adam Bard", "http://adambard.com/"]
translators:
- ["netpyoung", "http://netpyoung.github.io/"]
lang: ko-kr
---
Clojure는 Java 가상머신을 위해 개발된 Lisp 계통의 언어입니다
이는 Common Lisp보다 순수 [함수형 프로그래밍](https://en.wikipedia.org/wiki/Functional_programming)을 더욱 강조했으며,
상태를 있는 그대로 다루기 위해 다양한 [STM](https://en.wikipedia.org/wiki/Software_transactional_memory) 을 지원하는 프로그램들을 갖췄습니다.
이를 조합하여, 병행처리(concurrent processing)를 매우 단순하게 처리할 수 있으며,
대게 자동으로 처리될 수 있도록 만들 수 있습니다.
(Clojure 1.2 이상의 버전이 필요로 합니다.)
```clojure
; 주석은 세미콜론(;)으로 시작합니다.
; Clojure는 "폼(forms)"으로 구성되었으며,
; 폼은 괄호로 감싸져있으며, 공백으로 구분된 것들이 나열된 것입니다.
;
; clojure의 reader는 첫번째로 오는 것을
; 함수 혹은 매크로를 호출하는 것, 그리고 나머지를 인자라고 가정합니다.
; namespace를 지정하기 위해, 파일에서 우선적으로 호출해야될 것은 ns입니다.
(ns learnclojure)
; 간단한 예제들:
; str 은 인자로 받은 것들을 하나의 문자열로 만들어줍니다.
(str "Hello" " " "World") ; => "Hello World"
; 직관적인 수학 함수들을 갖고 있습니다.
(+ 1 1) ; => 2
(- 2 1) ; => 1
(* 1 2) ; => 2
(/ 2 1) ; => 2
; = 로 동일성을 판별할 수 있습니다.
(= 1 1) ; => true
(= 2 1) ; => false
; 논리연산을 위한 not 역시 필요합니다.
(not true) ; => false
; 중첩된 폼(forms)은 기대한대로 동작합니다.
(+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2
; 타입
;;;;;;;;;;;;;
; Clojure는 부울(boolean), 문자열, 숫자를 위해 Java의 object 타입을 이용합니다.
; `class` 를 이용하여 이를 확인할 수 있습니다.
(class 1) ; 정수는 기본적으로 java.lang.Long입니다.
(class 1.); 소수는 java.lang.Double입니다.
(class ""); 문자열은 쌍따옴표로 감싸져 있으며, java.lang.String입니다.
(class false) ; 부울값은 java.lang.Boolean입니다.
(class nil); nil은 "null"값입니다.
; 데이터 리스트 자체를 만들고자 한다면,
; '를 이용하여 평가(evaluate)되지 않도록 막아야 합니다.
'(+ 1 2) ; => (+ 1 2)
; (quote (+ 1 2)) 를 줄여서 쓴것
; quote 가 된 리스트를 평가할 수 도 있습니다.
(eval '(+ 1 2)) ; => 3
; 컬렉션(Collections) & 시퀀스(Sequences)
;;;;;;;;;;;;;;;;;;;
; 리스트(List)는 연결된(linked-list) 자료구조이며, 벡터(Vector)는 배열이 뒤로붙는(array-backed) 자료구조입니다.
; 리스트와 벡터 모두 java 클래스입니다!
(class [1 2 3]); => clojure.lang.PersistentVector
(class '(1 2 3)); => clojure.lang.PersistentList
; 간단하게 (1 2 3)로 리스트를 나타낼 수 있지만,
; reader가 함수라고 여기지 못하게 quote(')를 해줘야 합니다.
; 따라서, (list 1 2 3)는 '(1 2 3)와 같습니다.
; "컬렉션"은 단순하게 데이터의 그룹입니다.
; 리스트와 벡터 모두 컬렉션입니다:
(coll? '(1 2 3)) ; => true
(coll? [1 2 3]) ; => true
; "시퀀스" (seq) 는 데이터 리스트를 추상적으로 기술한 것입니다.
; 리스트는 시퀀스입니다.
(seq? '(1 2 3)) ; => true
(seq? [1 2 3]) ; => false
; 시퀀스는 접근하고자 하는 항목만 제공해주면 됩니다.
; 따라서, 시퀀스는 lazy 할 수 있습니다 -- 무한하게 늘어나는 것을 정의할 수 있습니다:
(range 4) ; => (0 1 2 3)
(range) ; => (0 1 2 3 4 ...) (an infinite series)
(take 4 (range)) ; (0 1 2 3)
; cons 를 이용하여 리스트나 벡터의 시작부에 항목을 추가할 수 있습니다.
(cons 4 [1 2 3]) ; => (4 1 2 3)
(cons 4 '(1 2 3)) ; => (4 1 2 3)
; conj 는 컬렉션에 가장 효율적인 방식으로 항목을 추가합니다.
; 리스트는 시작부분에 삽입하고, 벡터는 끝부분에 삽입합니다.
(conj [1 2 3] 4) ; => [1 2 3 4]
(conj '(1 2 3) 4) ; => (4 1 2 3)
; concat 을 이용하여 리스트와 벡터를 서로 합칠 수 있습니다.
(concat [1 2] '(3 4)) ; => (1 2 3 4)
; filter, map 을 이용하여 컬렉션을 다룰 수 있습니다.
(map inc [1 2 3]) ; => (2 3 4)
(filter even? [1 2 3]) ; => (2)
; reduce 를 이용하여 줄여나갈 수 있습니다.
(reduce + [1 2 3 4])
; = (+ (+ (+ 1 2) 3) 4)
; => 10
; reduce 는 초기 값을 인자로 취할 수 도 있습니다.
(reduce conj [] '(3 2 1))
; = (conj (conj (conj [] 3) 2) 1)
; => [3 2 1]
; 함수
;;;;;;;;;;;;;;;;;;;;;
; fn 을 이용하여 함수를 만들 수 있습니다 .
; 함수는 항상 마지막 문장을 반환합니다.
(fn [] "Hello World") ; => fn
; (정의한 것을 호출하기 위해선, 괄호가 더 필요합니다.)
((fn [] "Hello World")) ; => "Hello World"
; def 를 이용하여 var 를 만들 수 있습니다.
(def x 1)
x ; => 1
; var 에 함수를 할당시켜보겠습니다.
(def hello-world (fn [] "Hello World"))
(hello-world) ; => "Hello World"
; defn 을 이용하여 짧게 쓸 수 도 있습니다.
(defn hello-world [] "Hello World")
; [] 는 함수의 인자 목록을 나타냅니다.
(defn hello [name]
(str "Hello " name))
(hello "Steve") ; => "Hello Steve"
; 약자(shorthand)를 써서 함수를 만들 수 도 있습니다:
(def hello2 #(str "Hello " %1))
(hello2 "Fanny") ; => "Hello Fanny"
; 함수가 다양한 인자를 받도록 정의할 수 도 있습니다.
(defn hello3
([] "Hello World")
([name] (str "Hello " name)))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"
; 함수는 여러 인자를 시퀀스로 취할 수 있습니다.
(defn count-args [& args]
(str "You passed " (count args) " args: " args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
; 개별적으로 받는 것과, 시퀀스로 취하는 것을 같이 쓸 수 도 있습니다.
(defn hello-count [name & args]
(str "Hello " name ", you passed " (count args) " extra args"))
(hello-count "Finn" 1 2 3)
; => "Hello Finn, you passed 3 extra args"
; 맵(Maps)
;;;;;;;;;;
; 해쉬맵(hash map)과 배열맵(array map)은 공통된 인터페이스를 공유합니다.
; 해쉬맵은 찾기가 빠르지만, 키의 순서가 유지되지 않습니다.
(class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap
(class (hash-map :a 1 :b 2 :c 3)) ; => clojure.lang.PersistentHashMap
; 배열맵은 여러 연산을 거쳐 자연스레 해쉬맵이 됩니다.
; 만일 이게 커진다 하더라도, 걱정할 필요가 없습니다.
; 맵은 해쉬가 가능한 타입이라면 어떠한 것이든 키로써 활용이 가능하지만, 보통 키워드를 이용하는 것이 가장 좋습니다.
; 키워드(Keyword)는 문자열과 비슷하지만, 보다 효율적인 면이 있습니다.
(class :a) ; => clojure.lang.Keyword
(def stringmap {"a" 1, "b" 2, "c" 3})
stringmap ; => {"a" 1, "b" 2, "c" 3}
(def keymap {:a 1, :b 2, :c 3})
keymap ; => {:a 1, :c 3, :b 2}
; 여기서, 쉽표가 공백으로 취급되며, 아무 일도 하지 않는다는 것을 주목하시기 바랍니다.
; 맵에서 값을 얻어오기 위해선, 함수로써 맵을 호출해야 합니다.
(stringmap "a") ; => 1
(keymap :a) ; => 1
; 키워드 역시 맵에서 함수를 얻어올 때 사용할 수 있습니다!
(:b keymap) ; => 2
; 하지만, 문자열로는 하면 안됩니다.
;("a" stringmap)
; => Exception: java.lang.String cannot be cast to clojure.lang.IFn
; 없는 값을 얻어오고자 하면, nil이 반환됩니다.
(stringmap "d") ; => nil
; assoc 를 이용하여 해쉬맵에 새로운 키를 추가할 수 있습니다.
(def newkeymap (assoc keymap :d 4))
newkeymap ; => {:a 1, :b 2, :c 3, :d 4}
; 하지만, 변경할 수 없는(immutable) clojure 타입이라는 것을 기억해야 합니다!
keymap ; => {:a 1, :b 2, :c 3}
; dissoc 를 이용하여 키를 제거할 수 있습니다.
(dissoc keymap :a :b) ; => {:c 3}
; 쎗(Set:집합)
;;;;;;
(class #{1 2 3}) ; => clojure.lang.PersistentHashSet
(set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}
; conj 로 항목을 추가할 수 있습니다.
(conj #{1 2 3} 4) ; => #{1 2 3 4}
; disj 로 제거할 수 도 있습니다.
(disj #{1 2 3} 1) ; => #{2 3}
; 존재하는지 확인할 목적으로, 쎗을 함수로 사용할 수 도 있습니다.
(#{1 2 3} 1) ; => 1
(#{1 2 3} 4) ; => nil
; clojure.sets 네임스페이스(namespace)에는 더 많은 함수들이 있습니다.
; 유용한 폼(forms)
;;;;;;;;;;;;;;;;;
; clojure에선, if 와 매크로(macro)를 가지고,
; 다른 여러 논리 연산들을 만들 수 있습니다.
(if false "a" "b") ; => "b"
(if false "a") ; => nil
; let 을 이용하여 임시적으로 바인딩(binding)을 구축할 수 있습니다.
(let [a 1 b 2]
(> a b)) ; => false
; do 로 문단을 묶을 수 도 있습니다.
(do
(print "Hello")
"World") ; => "World" (prints "Hello")
; 함수는 암시적으로 do 를 가지고 있습니다.
(defn print-and-say-hello [name]
(print "Saying hello to " name)
(str "Hello " name))
(print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff")
; let 역시 그러합니다.
(let [name "Urkel"]
(print "Saying hello to " name)
(str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel")
; 모듈(Modules)
;;;;;;;;;;;;;;;
; "use" 를 이용하여 module에 있는 모든 함수들을 얻어올 수 있습니다.
(use 'clojure.set)
; 이제 쎗(set:집합)연산을 사용 할 수 있습니다.
(intersection #{1 2 3} #{2 3 4}) ; => #{2 3}
(difference #{1 2 3} #{2 3 4}) ; => #{1}
; 함수들 중에 일 부분만을 가져올 수 도 있습니다.
(use '[clojure.set :only [intersection]])
; require 를 이용하여 모듈을 import할 수 있습니다.
(require 'clojure.string)
; / 를 이용하여 모듈에 있는 함수를 호출 할 수 있습니다.
; 여기, clojure.string 라는 모듈에, blank? 라는 함수가 있습니다.
(clojure.string/blank? "") ; => true
; import시, 모듈에 짧은 이름을 붙여줄 수 있습니다.
(require '[clojure.string :as str])
(str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst."
; (#"" denotes a regular expression literal)
; :require 를 이용하여, 네임스페이스에서 require 를 사용할 수 있습니다.
; 아레와 같은 방법을 이용하면, 모듈을 quote하지 않아도 됩니다.
(ns test
(:require
[clojure.string :as str]
[clojure.set :as set]))
; Java
;;;;;;;;;;;;;;;;;
; Java는 유용한 많은 표준 라이브러리를 가지고 있으며,
; 이를 어떻게 활용할 수 있는지 알아보도록 하겠습니다.
; import 로 java 모듈을 불러올 수 있습니다.
(import java.util.Date)
; ns 와 함께 import 를 할 수 도 있습니다.
(ns test
(:import java.util.Date
java.util.Calendar))
; 새로운 인스턴스를 만들기 위해선, 클래스 이름 끝에 "."을 찍습니다.
(Date.) ; <a date object>
; . 을 이용하여 메소드를 호출할 수 있습니다.
; 아니면, 줄여서 ".메소드"로도 호출 할 수 있습니다.
(. (Date.) getTime) ; <a timestamp>
(.getTime (Date.)) ; exactly the same thing.
; / 를 이용하여 정적메소드를 호출 할 수 있습니다.
(System/currentTimeMillis) ; <a timestamp> (system is always present)
; doto 를 이용하여 상태가 변하는(mutable) 클래스들을 좀 더 편하게(tolerable) 다룰 수 있습니다.
(import java.util.Calendar)
(doto (Calendar/getInstance)
(.set 2000 1 1 0 0 0)
.getTime) ; => A Date. set to 2000-01-01 00:00:00
; STM
;;;;;;;;;;;;;;;;;
; Software Transactional Memory 는 clojure가 영구적인(persistent) 상태를 다루는 방식입니다.
; clojure가 이용하는 몇몇 자료형(construct)이 있습니다.
; 가장 단순한 것은 atom 입니다. 초기 값을 넣어보도록 하겠습니다.
(def my-atom (atom {}))
; swap! 으로 atom을 갱신(update)할 수 있습니다!
; swap! 은 함수를 인자로 받아, 그 함수에 대해 현재 atom에 들어있는 값을 첫번째 인자로,
; 나머지를 두번째 인자로 하여 호출합니다.
(swap! my-atom assoc :a 1) ; Sets my-atom to the result of (assoc {} :a 1)
(swap! my-atom assoc :b 2) ; Sets my-atom to the result of (assoc {:a 1} :b 2)
; '@' 를 이용하여 atom을 역참조(dereference)하여 값을 얻을 수 있습니다.
my-atom ;=> Atom<#...> (atom 객체가 반환됩니다.)
@my-atom ; => {:a 1 :b 2}
; 여기 atom을 이용한 단순한 카운터가 있습니다.
(def counter (atom 0))
(defn inc-counter []
(swap! counter inc))
(inc-counter)
(inc-counter)
(inc-counter)
(inc-counter)
(inc-counter)
@counter ; => 5
; STM을 구성하는 다른 것들에는 ref 와 agent 가 있습니다.
; Refs: http://clojure.org/refs
; Agents: http://clojure.org/agents
```
### 읽어볼거리
부족한 것이 많았지만, 다행히도 채울 수 있는 것들이 많이 있습니다.
Clojure.org에 많은 문서들이 보관되어 있습니다:
[http://clojure.org/](http://clojure.org/)
Clojuredocs.org는 core 함수들에 대해 다양한 예제와 문서를 보유하고 있습니다:
[http://clojuredocs.org/quickref/Clojure%20Core](http://clojuredocs.org/quickref/Clojure%20Core)
4Clojure는 clojure/FP 스킬을 올릴 수 있는 좋은 길입니다:
[https://4clojure.oxal.org/](https://4clojure.oxal.org/)
Clojure-doc.org는 많고 많은 문서들을 보유하고 있습니다:
[http://clojure-doc.org/](http://clojure-doc.org/)

58
ko/coffeescript.md Normal file
View File

@@ -0,0 +1,58 @@
---
language: CoffeeScript
category: language
contributors:
- ["Tenor Biel", "http://github.com/L8D"]
filename: coffeescript-kr.coffee
translators:
- ["wikibook", "http://wikibook.co.kr"]
lang: ko-kr
---
``` coffeescript
# 커피스크립트(CoffeeScript)는 최신 유행을 따르는 언어입니다.
# 커피스크립트는 여러 현대 언어의 트렌드를 따르는데,
# 그래서 주석을 작성할 때는 루비나 파이썬과 같이 해시를 씁니다.
###
블록 주석은 이처럼 작성하며, 자바스크립트 코드로 만들어지도록
'/ *'와 '* /'로 직접적으로 변환됩니다.
계속하기에 앞서 자바스크립트 시맨틱을 대부분 이해하고 있어야 합니다.
###
# 할당:
number = 42 #=> var number = 42;
opposite = true #=> var opposite = true;
# 조건문:
number = -42 if opposite #=> if(opposite) { number = -42; }
# 함수:
square = (x) -> x * x #=> var square = function(x) { return x * x; }
# 범위:
list = [1..5] #=> var list = [1, 2, 3, 4, 5];
# 객체:
math =
root: Math.sqrt
square: square
cube: (x) -> x * square x
#=> var math = {
# "root": Math.sqrt,
# "square": square,
# "cube": function(x) { return x * square(x); }
#}
# 가변 인자(splat):
race = (winner, runners...) ->
print winner, runners
# 존재 여부 확인:
alert "I knew it!" if elvis?
#=> if(typeof elvis !== "undefined" && elvis !== null) { alert("I knew it!"); }
# 배열 조건 제시법(comprehensions):
cubes = (math.cube num for num in list) #=> ...
```

684
ko/common-lisp.md Normal file
View File

@@ -0,0 +1,684 @@
---
language: "Common Lisp"
category: language
filename: commonlisp-kr.lisp
contributors:
- ["Paul Nathan", "https://github.com/pnathan"]
- ["Rommel Martinez", "https://ebzzry.io"]
translators:
- ["Eunpyoung Kim", "https://github.com/netpyoung"]
lang: ko-kr
---
커먼 리스프(Common Lisp, CL)는 다양한 산업 어플리케이션에 적합한 범용적인 멀티페러다임 언어입니다.
프로그래밍할 수 있는 프로그래밍 언어로서 자주 언급되곤 합니다.
처음 접하기 좋은책은 [Practical Common Lisp](http://www.gigamonkeys.com/book/)입니다.
또 다른 유명한 책으로는 최근에 나온 [Land of Lisp](http://landoflisp.com/)입니다.
베스트 프렉틱스에 관한 [Common Lisp Recipes](http://weitz.de/cl-recipes/) 책도 최근에 출판되었습니다.
```lisp
;;;-----------------------------------------------------------------------------
;;; 0. 구문 (Syntax, 신택스)
;;;-----------------------------------------------------------------------------
;;; 일반적인 폼(form)
;;; CL은 2개의 근본이 되는 구문 요소를 가집니다: ATOM 과 S-EXPRESSION.
;;; 일반적으로, 괄호로 묶인 S-expressions를 `폼(form)`이라고 부릅니다.
10 ; atom; 그 자체로 평가됩니다
:thing ; atom; 심볼 :thing로 평가됩니다
t ; atom, 참을 나타냅니다
(+ 1 2 3 4) ; s-expression
'(4 :foo t) ; s-expression
;;; 주석
;;; 한줄주석은 세미콜론으로 시작합니다( ; )
;;; 파일 단위로는 4개, 구획(section) 설명으로는 3개, 정의(definition) 안에서는 2개,
;;; 한 라인에 대해서는 1개를 사용합니다. 예를들면,
;;;; life.lisp
;;; Foo bar baz, because quu quux. Optimized for maximum krakaboom and umph.
;;; Needed by the function LINULUKO.
(defun meaning (life)
"LIFE의 의미를 계산하여 반환합니다."
(let ((meh "abc"))
;; Invoke krakaboom
(loop :for x :across meh
:collect x))) ; 값을 x에 저장한 다음, 이를 반환합니다.
;;; 반면, 블록주석은 자유롭게 쓸 수 있습니다.
;;; #| 와 |# 로 구역을 나눌 수 있습니다.
#| 이것은 블록 주석이며,
여러 줄로 쓸 수 있으며
#|
중첩하여 사용할 수 있습니다!
|#
|#
;;; 환경
;;; 여러 종류의 구현체들이 있습니다;
;;; 대부분 표준을 따르지만, SBCL이 처음 시작하기에 좋습니다.
;;; Quicklisp를 이용하여 서드파티 라이브러리들을 쉽게 설치할 수 있습니다.
;;; CL을 개발할때 텍스트 편집기와
;;; 읽고(Read) 평가하고(Eval) 출력(Print)을 반복(Loop)하는 REPL을 동시에 활용하여 개발합니다.
;;; REPL은 프로그램이 "실행되고 있는 중(live)"에 프로그램과 상호작용을 할 수 있도록 만들어 줍니다.
;;;-----------------------------------------------------------------------------
;;; 1. 주요 데이터 타입 및 연산자
;;;-----------------------------------------------------------------------------
;;; 심볼
'foo ; => FOO 자동으로 심볼이 대문자로 된 것을 주목하시기 바랍니다.
;;; INTERN은 문자열을 심볼로 만들어 줍니다.
(intern "AAAA") ; => AAAA
(intern "aaa") ; => |aaa|
;;; 숫자
9999999999999999999999 ; 정수
#b111 ; 2진수 => 7
#o111 ; 8진수 => 73
#x111 ; 16진수 => 273
3.14159s0 ; single
3.14159d0 ; double
1/2 ; 분수
#C(1 2) ; 복소수
;;; 함수 적용
;;; (f x y z ...)에서 f는 함수이며, x, y, z, ... 인자입니다.
(+ 1 2) ; => 3
;;; 데이터 그 자체를 만들기 원한다면,
;;; QUOTE를 이용하여 평가를 막을 수 있습니다.
(quote (+ 1 2)) ; => (+ 1 2)
(quote a) ; => A
;;; QUOTE을 짧게 쓰면 ( ' )입니다.
'(+ 1 2) ; => (+ 1 2)
'a ; => A
;;; 기본 산술 연산자
(+ 1 1) ; => 2
(- 8 1) ; => 7
(* 10 2) ; => 20
(expt 2 3) ; => 8 ;; exponentiation: 제곱
(mod 5 2) ; => 1 ;; modulo: 나머지 연산
(/ 35 5) ; => 7
(/ 1 3) ; => 1/3
(+ #C(1 2) #C(6 -4)) ; => #C(7 -2)
;;; 불리언(Boolean)
t ; 참 ; NIL이 아니면 참.
nil ; 거짓; 빈 리스트: () 역시 거짓.
(not nil) ; => T
(and 0 t) ; => T
(or 0 nil) ; => 0
;;; 문자
#\A ; => #\A
#\λ ; => #\GREEK_SMALL_LETTER_LAMDA
#\u03BB ; => #\GREEK_SMALL_LETTER_LAMDA
;;; 문자열은 고정된 길이의 배열속에 문자들이 들어있는 것입니다.
"Hello, world!"
"Benjamin \"Bugsy\" Siegel" ; 역슬래쉬(\)는 이스케이프 문자입니다.
;;; 문자열을 연결(concatenate)시킬 수 도 있습니다.
(concatenate 'string "Hello, " "world!") ; => "Hello, world!"
;;; 문자열을 마치 문자들이 나열된것처럼 취급할 수도 있습니다.
(elt "Apple" 0) ; => #\A
;;; FORMAT은 형식화된 출력을 만들기 위해 사용됩니다.
;;; 간단한 스트링 인터폴레이션(string interpolation)부터 반복문이나 조건문까지 다양한 기능을 제공합니다.
;;; FORMAT의 첫번째 인자는 포맷팅된 문자열이 어디로 갈지 결정합니다
;;; 만약 NIL이라면, FORMAT은 포맷팅된 문자열을 반환합니다;
;;; 만약 T라면, FORMAT은 표준 출력, 일반적으로 스크린에 출력한 다음 NIL을 반환합니다.
(format nil "~A, ~A!" "Hello" "world") ; => "Hello, world!"
(format t "~A, ~A!" "Hello" "world") ; => NIL
;;;-----------------------------------------------------------------------------
;;; 2. 변수
;;;-----------------------------------------------------------------------------
;;; DEFVAR와 DEFPARAMETER를 이용하여 전역 (동적 스코프) 변수를 만들 수 있습니다.
;;; 변수 이름은 다음을 제외한 모든 문자를 사용할 수 있습니다: ()",'`;#|\
;;; DEFVAR와 DEFPARAMETER의 차이점으로는, DEFVAR 표현식을 다시 평가하더라도 변수의 값이 변경되지 않는다는 것입니다.
;;; 반면 DEFPARAMETER는 변경됩니다.
;;; 관례상, 동적 스코프 변수는 이름에는 귀마개(earmuffs)를 씌워줍니다.
(defparameter *some-var* 5)
*some-var* ; => 5
;;; 유니코드 문자 역시 사용할 수 있습니다.
(defparameter *AΛB* nil)
;;; 이전에 바인딩되지 않은 변수에 접근하면 UNBOUND-VARIABLE 에러가 발생하지만, 이것은 정의된 동작입니다.
;;; 바인딩되지 않은 변수에는 접근하지 마세요.
;;; LET 으로 지역 바인딩을 만들 수 있습니다.
;;; 다음 코드에서 (let ...) 안에서 "dance with you"는 `me`로 바인딩됩니다.
;;; LET은 항상 LET 폼의 마지막 `form`의 값을 반환합니다.
(let ((me "dance with you")) me) ; => "dance with you"
;;;-----------------------------------------------------------------------------;
;;; 3. 구조체와 컬렉션
;;;-----------------------------------------------------------------------------;
;;; 구조체
(defstruct dog name breed age)
(defparameter *rover*
(make-dog :name "rover"
:breed "collie"
:age 5))
*rover* ; => #S(DOG :NAME "rover" :BREED "collie" :AGE 5)
(dog-p *rover*) ; => T
(dog-name *rover*) ; => "rover"
;;; DOG-P, MAKE-DOG, DOG-NAME은 모두 DEFSTRUCT에 의해 자동으로 생성됩니다.
;;; 페어(쌍, Pair)
;;; CONS는 페어를 만듭니다. CAR와 CDR은 페어의 head와 tail을 반환합니다.
(cons 'SUBJECT 'VERB) ; => '(SUBJECT . VERB)
(car (cons 'SUBJECT 'VERB)) ; => SUBJECT
(cdr (cons 'SUBJECT 'VERB)) ; => VERB
;;; 리스트
;;; 리스트는 CONS 페어로 만들어진 링크드 리스트 데이터 구조입니다.
;;; 리스트의 끝은 NIL (또는 '())로 표시됩니다.
(cons 1 (cons 2 (cons 3 nil))) ; => '(1 2 3)
;;; LIST는 리스트를 위한 가변인자 생성자입니다.
(list 1 2 3) ; => '(1 2 3)
;;; CONS에 첫번째 인자가 원자이고 두번째 인자는 리스트일때,
;;; CONS는 새로운 CONS-페어를 반환하게 되는데,
;;; 첫번째 인자를 첫번째 아이템으로, 두번째 인자를 CONS-페어의 나머지로 하게됩니다.
(cons 4 '(1 2 3)) ; => '(4 1 2 3)
;;; APPEND를 사용하여 리스트를 연결할 수 있습니다.
(append '(1 2) '(3 4)) ; => '(1 2 3 4)
;;; 또는 CONCATENATE
(concatenate 'list '(1 2) '(3 4)) ; => '(1 2 3 4)
;;; 리스트는 매우 중요한 타입이며, 다양한 기능들이 있습니다.
;;; 몇 가지 예를 들어보겠습니다:
(mapcar #'1+ '(1 2 3)) ; => '(2 3 4)
(mapcar #'+ '(1 2 3) '(10 20 30)) ; => '(11 22 33)
(remove-if-not #'evenp '(1 2 3 4)) ; => '(2 4)
(every #'evenp '(1 2 3 4)) ; => NIL
(some #'oddp '(1 2 3 4)) ; => T
(butlast '(subject verb object)) ; => (SUBJECT VERB)
;;; 벡터
;;; 벡터는 고정길이 배열입니다.
#(1 2 3) ; => #(1 2 3)
;;; CONCATENATE를 사용하여 벡터를 연결할 수 있습니다.
(concatenate 'vector #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)
;;; 배열
;;; 벡터와 스트링은 배열의 특이 케이스입니다.
;;; 2차원 배열
(make-array (list 2 2)) ; => #2A((0 0) (0 0))
(make-array '(2 2)) ; => #2A((0 0) (0 0))
(make-array (list 2 2 2)) ; => #3A(((0 0) (0 0)) ((0 0) (0 0)))
;;; 주의: MAKE-ARRAY의 기본 초기값은 구현체에 따라 다릅니다.
;;; 명시적으로 지정하려면 다음과 같이하면 됩니다:
(make-array '(2) :initial-element 'unset) ; => #(UNSET UNSET)
;;; 1, 1, 1에 있는 요소에 접근하기:
(aref (make-array (list 2 2 2)) 1 1 1) ; => 0
;;; 반환되는 값은 구현체에 따라 다릅니다:
;;; SBCL과 CCL에서는 0, ECL에서는 NIL
;;; 조절 가능한 벡터(adjustable vector)
;;; 조절 가능한 벡터는 고정길이 벡터와 동일한 출력 결과를 갖습니다.
(defparameter *adjvec* (make-array '(3) :initial-contents '(1 2 3)
:adjustable t :fill-pointer t))
*adjvec* ; => #(1 2 3)
;;; 새로운 요소 추가하기
(vector-push-extend 4 *adjvec*) ; => 3
*adjvec* ; => #(1 2 3 4)
;;; 셋(Set)은 단순히 리스트입니다:
(set-difference '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1)
(intersection '(1 2 3 4) '(4 5 6 7)) ; => 4
(union '(1 2 3 4) '(4 5 6 7)) ; => (3 2 1 4 5 6 7)
(adjoin 4 '(1 2 3 4)) ; => (1 2 3 4)
;;; 하지만, 많은 데이터 셋을 다룰 경우,
;;; 링크드리스트 보다 더 나은 데이터 구조를 필요로 할 것입니다.
;;; 딕션어리는 해쉬태이블로 만들어졌습니다.
;;; 해쉬 테이블 만들기
(defparameter *m* (make-hash-table))
;;; 값 설정
(setf (gethash 'a *m*) 1)
;;; 값 받아오기
(gethash 'a *m*) ; => 1, T
;;; CL의 표현식은 여러개의 값을 반환 할 수 있습니다.
(values 1 2) ; => 1, 2
;;; MULTIPLE-VALUE-BIND로 연결(bind)지을 수 있습니다.
(multiple-value-bind (x y)
(values 1 2)
(list y x))
; => '(2 1)
;;; GETHASH는 여러 값을 반환하는 함수의 예입니다.
;;; 첫번째 반환값은 해쉬 테이블의 키의 값입니다;
;;; 만약 키가 발견되지 않으면 NIL을 반환합니다.
;;; 두번째 반환값은 키가 해쉬 테이블에 존재하는지 여부를 결정합니다.
;;; 테이블에서 키를 찾지 못하면 NIL을 반환합니다.
;;; 이러한 동작은 키의 값이 실제로 NIL인지 확인할 수 있도록 해줍니다.
(gethash 'd *m*) ;=> NIL, NIL
;;; 키가 없을때를 대비한 기본값을 설정할 수 있습니다;
(gethash 'd *m* :not-found) ; => :NOT-FOUND
;;; 반환된 값들을 처리해보겠습니다.
(multiple-value-bind (a b)
(gethash 'd *m*)
(list a b))
; => (NIL NIL)
(multiple-value-bind (a b)
(gethash 'a *m*)
(list a b))
; => (1 T)
;;;-----------------------------------------------------------------------------
;;; 3. 함수
;;;-----------------------------------------------------------------------------
;;; 익명 함수를 만들기 위해 LAMBDA를 사용합니다.
;;; 함수는 항상 마지막 표현식의 값을 반환합니다.
;;; 함수의 출력방식은 구현체마다 다릅니다.
(lambda () "Hello World") ; => #<FUNCTION (LAMBDA ()) {1004E7818B}>
;;; 익명 함수를 호출하기 위해 FUNCALL을 사용합니다.
(funcall (lambda () "Hello World")) ; => "Hello World"
(funcall #'+ 1 2 3) ; => 6
;;; 리스트의 앞에 lambda표현식을 넣으면, 암시적으로 FUNCALL을 호출합니다.
((lambda () "Hello World")) ; => "Hello World"
((lambda (val) val) "Hello World") ; => "Hello World"
;;; 인자가 미리 주어져 있으면 FUNCALL을 사용하고, 그렇지 않으면 APPLY를 사용합니다.
(apply #'+ '(1 2 3)) ; => 6
(apply (lambda () "Hello World") nil) ; => "Hello World"
;;; 함수에 이름을 붙이려면 DEFUN을 사용합니다.
(defun hello-world () "Hello World")
(hello-world) ; => "Hello World"
;;; 위 정의에서 ()는 인자 리스트입니다.
(defun hello (name) (format nil "Hello, ~A" name))
(hello "Steve") ; => "Hello, Steve"
;;; 함수는 선택적(optional) 인자를 가질 수 있습니다; 기본값은 NIL입니다.
(defun hello (name &optional from)
(if from
(format t "Hello, ~A, from ~A" name from)
(format t "Hello, ~A" name)))
(hello "Jim" "Alpacas") ; => Hello, Jim, from Alpacas
;;; 기본값을 다음과 같이 지정할 수도 있습니다.
(defun hello (name &optional (from "The world"))
(format nil "Hello, ~A, from ~A" name from))
(hello "Steve") ; => Hello, Steve, from The world
(hello "Steve" "the alpacas") ; => Hello, Steve, from the alpacas
;;; 함수는 키워드 인자를 이용하여 위치와 상관없는 인자를 가질 수도 있습니다.
(defun generalized-greeter (name &key (from "the world") (honorific "Mx"))
(format t "Hello, ~A ~A, from ~A" honorific name from))
(generalized-greeter "Jim")
; => Hello, Mx Jim, from the world
(generalized-greeter "Jim" :from "the alpacas you met last summer" :honorific "Mr")
; => Hello, Mr Jim, from the alpacas you met last summer
;;;-----------------------------------------------------------------------------
;;; 4. 동등성(Equality)
;;;-----------------------------------------------------------------------------
;;; CL은 정교한 동등성 시스템을 가지고 있습니다.
;;; 그 중 일부를 여기서 다뤄보도록 하겠습니다.
;;; 숫자에 대해서는 ( = )를 사용합니다.
(= 3 3.0) ; => T
(= 2 1) ; => NIL
;;; 객체 식별에 대해서는 EQL을 사용합니다.
(eql 3 3) ; => T
(eql 3 3.0) ; => NIL
(eql (list 3) (list 3)) ; => NIL
;;; 리스트, 스트링, 비트-벡터에 대해서는 EQUAL을 사용합니다.
(equal (list 'a 'b) (list 'a 'b)) ; => T
(equal (list 'a 'b) (list 'b 'a)) ; => NIL
;;;-----------------------------------------------------------------------------
;;; 5. 제어 흐름(Control Flow)
;;;-----------------------------------------------------------------------------
;;; 조건문
(if t ; 구문: 조건
"참입니다" ; 구문: 그러면
"거짓입니다") ; 구문: 그렇지 않으면
; => "참입니다"
;;; 조건문에서, NIL이 아닌 모든 값은 참으로 취급됩니다.
(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(GROUCHO ZEPPO)
(if (member 'Groucho '(Harpo Groucho Zeppo))
'yep
'nope)
; => 'YEP
;;; COND는 일련의 테스트를 실행하며, 결과를 선택합니다.
(cond ((> 2 2) (error "wrong!"))
((< 2 2) (error "wrong again!"))
(t 'ok)) ; => 'OK
;;; TYPECASE는 값의 타입에 따라 분기합니다.
(typecase 1
(string :string)
(integer :int))
; => :int
;;; 루프(loop)
;;; 재귀(recursion)
(defun fact (n)
(if (< n 2)
1
(* n (fact(- n 1)))))
(fact 5) ; => 120
;;; 반복(iteration)
(defun fact (n)
(loop :for result = 1 :then (* result i)
:for i :from 2 :to n
:finally (return result)))
(fact 5) ; => 120
(loop :for x :across "abcd" :collect x)
; => (#\a #\b #\c #\d)
(dolist (i '(1 2 3 4))
(format t "~A" i))
; => 1234
;;;-----------------------------------------------------------------------------
;;; 6. 변경(Mutation)
;;;-----------------------------------------------------------------------------
;;; 기존 변수에 새 값을 할당하기 위해선 SETF를 사용합니다.
;;; 해쉬 테이블 예제에서도 한번 나왔었습니다.
(let ((variable 10))
(setf variable 2))
; => 2
;;; 좋은 리스프 스타일은 파괴적인 함수의 사용을 최소화하고,
;;; 변경을 되도록 피하는 것입니다.
;;;-----------------------------------------------------------------------------
;;; 7. 클래스와 객체
;;;-----------------------------------------------------------------------------
;;; 더 이상 animal 클래스는 없습니다.
;;; 인간을 동력수단으로 삼는 운송기계
;;; (Human-Powered Mechanical Conveyances)를 만들어보겠습니다.
(defclass human-powered-conveyance ()
((velocity
:accessor velocity
:initarg :velocity)
(average-efficiency
:accessor average-efficiency
:initarg :average-efficiency))
(:documentation "A human powered conveyance"))
;;; DEFCLASS의 인자의 순서는 다음과 같습니다:
;;; 1. 클래스 이름
;;; 2. 슈퍼클래스 목록
;;; 3. 슬롯 목록
;;; 4. 선택적 지정자
;;; 이 때 슈퍼클래스 목록이 설정되지 않으면, 빈 목록이 표준 객체 클래스로 기본 설정됩니다.
;;; 이것은 변경할 수도 있지만, 어떻게 돌아가는지 알기전에는 변경하지 않습니다.
;;; 그러면, 메타오브젝트 프로토콜의 예술(Art of the Metaobject Protocol)에 대해 좀 더 살펴보도록 하겠습니다.
(defclass bicycle (human-powered-conveyance)
((wheel-size
:accessor wheel-size
:initarg :wheel-size
:documentation "Diameter of the wheel.")
(height
:accessor height
:initarg :height)))
(defclass recumbent (bicycle)
((chain-type
:accessor chain-type
:initarg :chain-type)))
(defclass unicycle (human-powered-conveyance) nil)
(defclass canoe (human-powered-conveyance)
((number-of-rowers
:accessor number-of-rowers
:initarg :number-of-rowers)))
;;; REPL에서 HUMAN-POWERED-CONVEYANCE 클래스에 대해 DESCRIBE를 호출하면 다음과 같은 결과를 얻게됩니다:
(describe 'human-powered-conveyance)
; COMMON-LISP-USER::HUMAN-POWERED-CONVEYANCE
; [symbol]
;
; HUMAN-POWERED-CONVEYANCE names the standard-class #<STANDARD-CLASS
; HUMAN-POWERED-CONVEYANCE>:
; Documentation:
; A human powered conveyance
; Direct superclasses: STANDARD-OBJECT
; Direct subclasses: UNICYCLE, BICYCLE, CANOE
; Not yet finalized.
; Direct slots:
; VELOCITY
; Readers: VELOCITY
; Writers: (SETF VELOCITY)
; AVERAGE-EFFICIENCY
; Readers: AVERAGE-EFFICIENCY
; Writers: (SETF AVERAGE-EFFICIENCY)
;;; 주목할 점은 리플렉션이 가능하다는 것입니다.
;;; CL은 대화형 시스템으로 설계되었습니다.
;;; 메서드를 정의하기 앞서, 자전거 바퀴의 둘레가 얼마나 되는 공식을 살펴봅시다:
;;; C = d * pi
(defmethod circumference ((object bicycle))
(* pi (wheel-size object)))
;;; PI는 CL에 미리 정의되어 있습니다.
;;; 카누에 있는 노 젓는 사람들의 수의 효율성 값이 대략 로그함수적이라는 것을 안다고 가정해봅시다.
;;; 이와 같은 정보는 생성자/초기화자에서 설정하는 것이 좋습니다.
;;; CL이 인스턴스를 생성한 다음에(after), 인스턴스를 초기화하기 위해선:
(defmethod initialize-instance :after ((object canoe) &rest args)
(setf (average-efficiency object) (log (1+ (number-of-rowers object)))))
;;; 그런 다음 인스턴스를 생성하고, 평균 효율을 확인합니다...
(average-efficiency (make-instance 'canoe :number-of-rowers 15))
; => 2.7725887
;;;-----------------------------------------------------------------------------
;;; 8. 매크로
;;;-----------------------------------------------------------------------------
;;; 매크로는 언어의 구문을 확장할 수 있게 해줍니다.
;;; CL에는 WHILE 루프가 없지만, 새로 작성하는 것은 간단합니다.
;;; 어셈블러의 명령어를 따라가자면, 다음과 같이 될 것입니다:
(defmacro while (condition &body body)
"While `condition` is true, `body` is executed.
`condition` is tested prior to each execution of `body`"
(let ((block-name (gensym)) (done (gensym)))
`(tagbody
,block-name
(unless ,condition
(go ,done))
(progn
,@body)
(go ,block-name)
,done)))
;;; 좀 더 고수준 버전을 살펴보겠습니다:
(defmacro while (condition &body body)
"While `condition` is true, `body` is executed.
`condition` is tested prior to each execution of `body`"
`(loop while ,condition
do
(progn
,@body)))
;;; 하지만, 현대의 컴파일러에서는 이것이 필요하지 않습니다;
;;; LOOP 폼은 동일하게 잘 컴파일되며 읽기 쉽습니다.
;;; Note that ``` is used, as well as `,` and `@`. ``` is a quote-type operator
;;; known as quasiquote; it allows the use of `,` . `,` allows "unquoting"
;;; variables. @ interpolates lists.
;;; GEMSYM은 다른 곳에서 사용되지 않는 것이 보장된 시스템에서 유일한 심볼을 생성합니다.
;;; 컴파일 타임에 매크로가 확장되는데, 매크로에서 선언된 변수가
;;; 일반 코드에서 사용되는 변수와 충돌할 가능성이 있기 때문입니다.
;;; 매크로에 대해 더 자세한 정보를 얻고 싶으시다면, Practical Common Lisp와 On Lisp를 살펴보시기 바랍니다.
```
## 더 읽어볼거리
- [Practical Common Lisp](http://www.gigamonkeys.com/book/)
- [Common Lisp: A Gentle Introduction to Symbolic Computation](https://www.cs.cmu.edu/~dst/LispBook/book.pdf)
## 추가 정보
- [CLiki](http://www.cliki.net/)
- [common-lisp.net](https://common-lisp.net/)
- [Awesome Common Lisp](https://github.com/CodyReichert/awesome-cl)
- [Lisp Lang](http://lisp-lang.org/)
## 크레딧
Scheme 사용자들의 노고에 큰 감사를 드립니다. 좋은 시작점을 만들어 주신 덕분에 쉽게 Common Lisp로 옮길 수 있었습니다.
- 훌륭한 리뷰를 해주신 [Paul Khuong](https://github.com/pkhuong)

332
ko/erlang.md Normal file
View File

@@ -0,0 +1,332 @@
---
language: Erlang
contributors:
- ["Giovanni Cappellotto", "http://www.focustheweb.com/"]
filename: learnerlang-kr.erl
translators:
- ["Taesung Jung", "https://github.com/tsj"]
lang: ko-kr
---
```erlang
% 퍼센트 기호는 한 줄 주석을 시작한다.
%% 두 개의 퍼센트 문자는 함수의 주석에 사용된다.
%%% 세 개의 퍼센트 문자는 모듈의 주석에 사용된다.
% Erlang에선 3가지 유형의 문장 부호를 사용한다.
% 쉼표(`,`)는 함수 호출에서 인수, 데이터 생성자(constructors), 패턴을 구분한다.
% 마침표(`.`)(다음에 오는 공백)는 셸에서 함수 전체와 식을 구분한다.
% 세미콜론(`;`)은 절을 구분한다. 몇 가지 문맥(contexts)에서 절이 발견된다:
% 함수 정의와 `case`, `if`, `try..catch`, 그리고 `receive` 식
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 1. 변수와 패턴 매칭
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Erlang에서 새로운 변수는 `=` 문장에 의해 바인딩 된다.
Num = 42. % 모든 변수 이름은 반드시 대문자로 시작해야 한다.
% Erlang은 단일 할당 변수(single-assignment variables)를 가진다;
% 만약 다른 값을 `Num` 변수에 할당하려고 시도하면 오류가 발생한다.
Num = 43. % ** 예외 오류: 우변의 값 43과 매칭되지 않음
% 대부분 언어에서 `=`는 할당문을 나타낸다. 그러나 Erlang에서
% `=`는 패턴 매칭 연산자를 나타낸다. 비어 있는 변수가 `=` 연산자의 좌변에
% 사용되면 바인드(할당) 된다, 그러나 바인드 변수가 좌변에 사용된 경우에
% 다음 행동은 그 바인드 변수가 관측된다.
% `Lhs = Rhs`의 진짜 의미: 우변(`Rhs`)을 평가하고, 그리고
% 그 결과를 좌변(`Lhs`)의 패턴과 매치시켜라.
Num = 7 * 6.
% 부동 소수점 수.
Pi = 3.14159.
% Atom은 숫자가 아닌 서로 다른 상숫값을 표현하는 데 사용한다. Atom은
% 소문자로 시작하고, 연속적인 영숫자(alphanumeric) 문자나 밑줄(`_`) 또는
% 골뱅이(`@`) 기호가 따라온다.
Hello = hello.
OtherNode = example@node.
% 영숫자 값이 아닌 Atom은 작은따옴표로 묶여서 작성될 수 있다.
AtomWithSpace = 'some atom with space'.
% Tuple은 C의 struct와 비슷하다.
Point = {point, 10, 45}.
% Tuple에서 어떤 값을 추출하려면, 패턴 매칭 연산자 `=`를 사용한다.
{point, X, Y} = Point. % X = 10, Y = 45
% 관심 없는 변수를 위해 자리 표시자(placeholder) `_`를 사용할 수 있다.
% 기호 `_`는 익명 변수(anonymous variable)라 부른다. 일반적인 변수들과
% 다르게 같은 패턴에서 여러 번 나오더라도 동일한 값으로 바인드되지 않아도 된다.
Person = {person, {name, {first, joe}, {last, armstrong}}, {footsize, 42}}.
{_, {_, {_, Who}, _}, _} = Person. % Who = joe
% List를 만들기 위해서 List의 원소는 대괄호([])로 둘러싸고 쉼표(,)로 구분한다.
% List의 각각의 원소는 어떤 타입도 가능하다.
% List의 첫 번째 원소는 List의 HEAD이다. 만약 List의 HEAD를 제거하면,
% 남은 부분은 List의 TAIL이라 부른다.
ThingsToBuy = [{apples, 10}, {pears, 6}, {milk, 3}].
% 만약 `T`가 List이면, `[H|T]`도 HEAD가 `H`이고 TAIL이 `T`인 List이다.
% 세로 막대(`|`)는 List의 HEAD와 TAIL을 분리한다. `[]`는 빈 List다.
% List의 원소들은 패턴 매칭 연산으로 추출할 수 있다.
% 만약 비어있지 않은 List `L`이 있을 때, `[X|Y] = L` 식의 `X`와 `Y`가
% 바인드되지 않은 변수이면, List의 HEAD는 X에 그리고 TAIL은 Y로 추출된다.
[FirstThing|OtherThingsToBuy] = ThingsToBuy.
% FirstThing = {apples, 10}
% OtherThingsToBuy = [{pears, 6}, {milk, 3}]
% Erlang에는 문자열(String)이 없다. 문자열은 사실 정수의 List일 뿐이다.
% 문자열은 큰따옴표(`"`)로 묶인다.
Name = "Hello".
[72, 101, 108, 108, 111] = "Hello".
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 2. 순차 프로그래밍
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Erlang에서 Module은 코드의 기본 단위이다. 우리가 작성한 모든 함수는
% Module에 담긴다. Module은 확장자가 `.erl`인 파일에 저장된다.
% 코드가 실행되기 전에 Module은 컴파일되어야 한다. 컴파일된 Module은
% `.beam` 확장자를 가진다.
-module(geometry).
-export([area/1]). % Module로부터 내보내진(exported) 함수의 List
% 함수 `area`는 두 개의 절로 구성된다. 절은 세미콜론(`;`)으로 구분되며,
% 마지막 절은 마침표-공백(dot-whitespace)으로 끝난다.
% 각 절은 서문(head)과 본문(body)을 가진다. 서문은 함수의 이름에 이어서
% 패턴이(괄호 속에) 따라온다. 본문은 연속적인 식으로 구성되고,
% 연속적인 식은 서문의 패턴과 호출한 인수가 성공적으로 매치되면 평가된다.
% 패턴은 함수 정의가 나타나는 순서대로 매치된다.
area({rectangle, Width, Ht}) -> Width * Ht;
area({circle, R}) -> 3.14159 * R * R.
% geometry.erl 파일의 코드 컴파일
c(geometry). % {ok,geometry}
% 호출하려는 함수를 정확히 알아내기 위해 함수 이름을 Module 이름과 함께
% 명시하는 것이 필요하다.
geometry:area({rectangle, 10, 5}). % 50
geometry:area({circle, 1.4}). % 6.15752
% Erlang에서, 같은 Module에 이름이 같고 Arity(인수의 갯수)가 다른
% 두 함수는 전혀 다른 함수를 나타낸다.
-module(lib_misc).
-export([sum/1]). % Arity가 1인 내보내진(export) 함수 `sum`
% 하나의 인수만 받음: 정수의 List
sum(L) -> sum(L, 0).
sum([], N) -> N;
sum([H|T], N) -> sum(T, H+N).
% Fun은 "익명(anonymous)" 함수다. 이름이 없어서 이렇게 부른다.
% 그러나, 변수에 할당될 수 있다.
Double = fun(X) -> 2 * X end. % `Double`은 익명 함수를 가리킨다:
% #Fun<erl_eval.6.17052888>
Double(2). % 4
% 함수는 인수로 Fun을 받거나, Fun을 반환할 수 있다.
Mult = fun(Times) -> ( fun(X) -> X * Times end ) end.
Triple = Mult(3).
Triple(5). % 15
% List 해석(List comprehensions)은 Fun, Map, Filter 없이 List를 만드는 식이다.
% 표기법 `[F(X) || X <- L]`은 `F(X)`의 List라는 의미이다.
% 이때 `X`는 List `L`로부터 가져온다.
L = [1,2,3,4,5].
[2 * X || X <- L]. % [2,4,6,8,10]
% List 해석은 Generator와 생성된 값들의 부분 집합을 선택하는 Filter를 가질 수 있다.
EvenNumbers = [N || N <- [1, 2, 3, 4], N rem 2 == 0]. % [2, 4]
% Guard는 패턴 매칭의 능력을 향상시키는데 사용할 수 있는 구조다.
% Guard를 사용하면, 패턴에 있는 변수에 대해 간단한 검사와 비교를 수행할 수 있다.
% 함수 정의의 서문(head)에 `when` 키워드로 시작되는 Guard를 사용할 수도 있고,
% 또는 식이 허용되는 언어의 어떤 곳에도 사용될 수 있다.
max(X, Y) when X > Y -> X;
max(X, Y) -> Y.
% Guard는 쉼표(`,`)로 구분된 연속된 Guard 식이다.
% 모든 Guard 식 `GuardExpr1`, `GuardExpr2`, ..., `GuardExprN`이
% `true`로 평가된다면, Guard `GuardExpr1`, `GuardExpr2`, ..., `GuardExprN`는
% 참이다.
is_cat(A) when is_atom(A), A =:= cat -> true;
is_cat(A) -> false.
is_dog(A) when is_atom(A), A =:= dog -> true;
is_dog(A) -> false.
% `=:=` 연산자는 여기서 자세히 다루지 않을 것이다; 두 개의 Erlang 식의 값이 같고
% *그리고* 같은 타입인지 검사하는 데 사용된다고만 알면 된다.
% `==` 연산자의 작동과 대조할 것:
1 + 2 =:= 3. % true
1 + 2 =:= 3.0. % false
1 + 2 == 3.0. % true
% 연속적인 Guard는 단일 Guard 또는 세미콜론(`;`)으로 구분된 연속된 Guard다.
% Guard `G1; G2; ...; Gn` 중에 적어도 하나의 Guard가 `true`로 평가된다면,
% 연속적인 Guard `G1; G2; ...; Gn`는 참이다.
is_pet(A) when is_atom(A), (A =:= dog);(A =:= cat) -> true;
is_pet(A) -> false.
% 주의: 모든 유효한 Erlang 식이 Guard 식으로 사용될 수 있는 것은 아니다;
% 특히, 함수 `is_cat`과 `is_dog`는 `is_pet`의 정의 안에 있는
% 연속적인 Guard 사이에 사용될 수 없다.
% 연속적인 Guard에 허용되는 식의 자세한 설명은 Erlang 레퍼런스 메뉴얼
% [section](http://erlang.org/doc/reference_manual/expressions.html#id81912)
% 을 참조하라.
% Record는 Tuple 안에 이름과 특정 요소를 연결하는 방법을 제공한다.
% Record 정의는 Erlang 소스 코드 파일에 포함되거나 Erlang 소스 코드 파일에
% 포함될 수 있는 확장자가 `.hrl`인 파일에 집어넣을 수 있다.
-record(todo, {
status = reminder, % 기본 값
who = joe,
text
}).
% Record를 사용할 수 있기 전에 Record 정의를 반드시 셸로 읽어 들여야 한다.
% 셸로 읽어 들이기 위해 셸 함수 `rr`(read records의 약자)을 사용한다.
rr("records.hrl"). % [todo]
% Record 생성과 수정
X = #todo{}.
% #todo{status = reminder, who = joe, text = undefined}
X1 = #todo{status = urgent, text = "Fix errata in book"}.
% #todo{status = urgent, who = joe, text = "Fix errata in book"}
X2 = X1#todo{status = done}.
% #todo{status = done, who = joe, text = "Fix errata in book"}
% `case` 식
% `filter`는 List `L`의 원소 `X` 중에서 `P(X)`가 참인 모든 `X`의 List를 반환한다.
filter(P, [H|T]) ->
case P(H) of
true -> [H|filter(P, T)];
false -> filter(P, T)
end;
filter(P, []) -> [].
filter(fun(X) -> X rem 2 == 0 end, [1, 2, 3, 4]). % [2, 4]
% `if` 식.
max(X, Y) ->
if
X > Y -> X;
X < Y -> Y;
true -> nil
end.
% 주의: 적어도 if 식의 Guard 중의 하나는 반드시 `true`로 평가되어야 한다.
% 그렇지 않으면 예외가 발생한다.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 3. 예외
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 예외는 내부에 에러가 생겼거나 명시적으로 `throw(Exception)`,
% `exit(Exception)` 또는 `erlang:error(Exception)`를 호출하면
% 시스템에 의해 발생한다.
generate_exception(1) -> a;
generate_exception(2) -> throw(a);
generate_exception(3) -> exit(a);
generate_exception(4) -> {'EXIT', a};
generate_exception(5) -> erlang:error(a).
% Erlang은 예외를 잡는 두 가지 방법을 가지고 있다. 한 가지는
% 예외를 발생시키는 함수의 호출 부분을 `try...catch` 식으로 감싸는 것이다.
catcher(N) ->
try generate_exception(N) of
Val -> {N, normal, Val}
catch
throw:X -> {N, caught, thrown, X};
exit:X -> {N, caught, exited, X};
error:X -> {N, caught, error, X}
end.
% 다른 방법은 그 호출 부분을 `catch` 식으로 감싸는 것이다.
% 예외를 잡았을 때, 그 예외는 오류를 설명하는 Tuple로 변환된다.
catcher(N) -> catch generate_exception(N).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 4. 병행성
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Erlang은 병행성을 위해 Actor 모델을 사용한다. Erlang에서 병행 프로그램을
% 작성하는 데 필요한 모든 것은 3가지 기본 형식(primitivies)이다:
% 프로세스 생성, 메시지 보내기, 메시지 받기
% 새로운 프로세스를 시작하기 위해, 함수를 인수로 받는 `spawn` 함수를 사용한다.
F = fun() -> 2 + 2 end. % #Fun<erl_eval.20.67289768>
spawn(F). % <0.44.0>
% `spawn`은 pid(프로세스 식별자)를 반환한다. 이 pid를 프로세스로
% 메시지를 보내는 데 사용할 수 있다. 메시지 전달을 위해, `!` 연산자를 사용한다.
% 위의 기능이 유용하려면, 메시지를 받을 수 있어야 한다. 메시지를 받는 것은
% `receive` 메커니즘을 사용한다.
-module(calculateGeometry).
-compile(export_all).
calculateArea() ->
receive
{rectangle, W, H} ->
W * H;
{circle, R} ->
3.14 * R * R;
_ ->
io:format("We can only calculate area of rectangles or circles.")
end.
% Module을 컴파일하고 셸에서 `calculateArea`를 평가한 프로세스를 생성한다.
c(calculateGeometry).
CalculateArea = spawn(calculateGeometry, calculateArea, []).
CalculateArea ! {circle, 2}. % 12.56000000000000049738
% 셸도 마찬가지로 프로세스이다. 현재 pid를 얻기 위해서 `self`를 사용할 수 있다.
self(). % <0.41.0>
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% 5. EUnit과 테스트
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% EUnit의 테스트 생성기(generators)와 assert 매크로를 이용해
% 단위 테스트를 작성할 수 있다.
-module(fib).
-export([fib/1]).
-include_lib("eunit/include/eunit.hrl").
fib(0) -> 1;
fib(1) -> 1;
fib(N) when N > 1 -> fib(N-1) + fib(N-2).
fib_test_() ->
[?_assert(fib(0) =:= 1),
?_assert(fib(1) =:= 1),
?_assert(fib(2) =:= 2),
?_assert(fib(3) =:= 3),
?_assert(fib(4) =:= 5),
?_assert(fib(5) =:= 8),
?_assertException(error, function_clause, fib(-1)),
?_assert(fib(31) =:= 2178309)
].
% EUnit은 Erlang 셸에서 테스트를 실행할 수 있게
% 자동으로 test() 함수를 내보낸다(export).
fib:test()
% Erlang의 유명한 빌드 툴인 Rebar는 EUnit과 호환된다.
% ```
% rebar eunit
% ```
```
## 참조
* ["Learn You Some Erlang for great good!"](http://learnyousomeerlang.com/)
* ["Programming Erlang: Software for a Concurrent World" by Joe Armstrong](http://pragprog.com/book/jaerlang/programming-erlang)
* [조 암스트롱, 김석준 역, "프로그래밍 얼랭: Software for a Concurrent World", 인사이트](http://ebook.insightbook.co.kr/book/23)
* [Erlang/OTP Reference Documentation](http://www.erlang.org/doc/)
* [Erlang - Programming Rules and Conventions](http://www.erlang.se/doc/programming_rules.shtml)

345
ko/go.md Normal file
View File

@@ -0,0 +1,345 @@
---
category: language
language: Go
filename: learngo-kr.go
contributors:
- ["Sonia Keys", "https://github.com/soniakeys"]
- ["Christopher Bess", "https://github.com/cbess"]
- ["Jesse Johnson", "https://github.com/holocronweaver"]
- ["Quint Guvernator", "https://github.com/qguv"]
translators:
- ["Jongmin Kim", "http://github.com/atomaths"]
- ["Peter Lee", "http://github.com/ins429"]
lang: ko-kr
---
Go는 어떤 일을 잘 끝낼 수 있도록 하기위해 만들어졌다. Go가 잘 알려진 최신의
트렌드는 아니지만, 실세계의 문제들을 해결하기 위해서는 가장
새롭고 빠른 방법이다.
Go는 정적 타이핑(static typing)의 명령형 언어들(imperative languages)이
갖고 있는 특징과 유사한 개념들을 가지고 있다. Go는 컴파일과 실행속도가
빠르며, 오늘날의 멀티코어 CPU를 위해 이해하기 쉬운 동시성(concurrency)
기능이 추가되었다. 그리고 큰 스케일의 프로그래밍에도 도움이 되는
기능들을 가지고 있다.
또한 Go에는 훌륭한 표준 라이브러리와 열정적인 커뮤니티가 있다.
```go
// 한 줄 주석
/* 여러 줄
주석 */
// 모든 Go 소스 파일은 package로 시작한다.
// 패키지 이름 중 main은 라이브러리가 아닌 실행파일을 선언하는 특별한 이름이다.
package main
// import는 이 Go 소스 파일 내에서 참조하는 라이브러리 패키지들을 선언한다.
import (
"fmt" // Go 표준 라이브러리에 있는 패키지
"net/http" // 표준 라이브러리에는 웹 서버 패키지도 있다! (클라이언트도 있음)
"strconv" // 문자열 변환 패키지
)
// 함수 선언. main은 실행 프로그램에서 시작점이 되는 특별한 함수다.
// 중괄호를 사용한다.
func main() {
// Println은 표준 출력으로 개행을 출력한다.
// fmt 패키지를 통해 이용할 수 있다.
fmt.Println("Hello world!")
// 다른 함수를 호출한다.
beyondHello()
}
// 함수에 파라미터가 없더라도 빈 괄호는 있어야 한다.
func beyondHello() {
var x int // 변수 선언. 변수는 사용하기 전에 선언해야 한다.
x = 3 // 변수에 값 할당.
// 짧은 선언(short declaration)으로 := 를 사용하는데,
// 이렇게 값을 할당하면 값의 타입에 따라 변수의 타입이 결정된다.
y := 4
sum, prod := learnMultiple(x, y) // 함수는 두 개 이상의 리턴 값을 줄 수 있다.
fmt.Println("sum:", sum, "prod:", prod) // 간단한 출력
learnTypes() // 잠시 후에 좀더 자세히!
}
// 함수는 파라미터들을 가질 수 있고, 복수개의 값을 리턴할 수 있다.
func learnMultiple(x, y int) (sum, prod int) {
return x + y, x * y // 두 개의 값을 리턴.
}
// 내장 타입과 리터럴
func learnTypes() {
// 짧은 선언은 유용하다.
s := "Learn Go!" // string 타입
s2 := `역따옴표 안의 string 리터럴은
개행을 포함할 수 있다.` // 같은 string 타입
// non-ASCII 리터럴. Go 소스는 UTF-8로 작성해야 한다.
g := 'Σ' // 유니코드 코드 포인트를 담고 있고, int32 타입의 가칭(alias)인 rune 타입
f := 3.14159 // float64, an IEEE-754 64-bit 부동소수 타입
c := 3 + 4i // complex128, 내부적으로는 두 개의 float64 타입으로 표현됨
// 초기값과 함께 사용하는 var 키워드.
var u uint = 7 // unsigned, 하지만 int에 따른 구현의존적인 크기
var pi float32 = 22. / 7
// 짧은 선언으로 변환(conversion)하는 문법.
// Go에서는 type casting 이라고 하지않고 type conversion 이라고 함.
n := byte('\n') // byte는 uint8의 가칭(alias)
// 배열은 컴파일 시에 크기가 정해진다.
var a4 [4]int // 모두 0으로 초기화되는 int 타입 4개짜리 배열
a3 := [...]int{3, 1, 5} // 3, 1, 5로 초기화되는 int 타입 3개짜리 배열
// 슬라이스(slice)라고 하는 타입은 배열에 대한 가변 크기를 가진다.
// 배열, 슬라이스 각자 장점이 있지만, 슬라이스가 더 많이 사용된다.
s3 := []int{4, 5, 9} // 위의 a3와 비교해보면 생략부호(...)가 없다.
s4 := make([]int, 4) // 모두 0으로 초기화되는 int 4개에 대한 슬라이스를 할당.
var d2 [][]float64 // 여기에서는 선언만 있고 할당은 없다.
bs := []byte("a slice") // string 타입을 byte 슬라이스 타입으로 형변환(type conversion)
p, q := learnMemory() // int에 대한 포인터 타입인 p와 q를 선언
fmt.Println(*p, *q) // C에서처럼 *는 포인터를 따라가 값을 참조한다. 여기서는 두 개의 int를 출력.
// 맵(map)은 다른 언어의 해시(hash)나 딕셔너리(dictionary)처럼 가변의 연관배열 타입.
m := map[string]int{"three": 3, "four": 4}
m["one"] = 1
// 선언만 하고 사용하지 않는 변수가 있다면 Go에서는 컴파일 시 에러가 난다.
// 언더바를 이용해서 변수를 사용한 것처럼 하고 그 값은 무시해버릴 수 있다.
_, _, _, _, _, _, _, _, _ = s2, g, f, u, pi, n, a3, s4, bs
// 물론 출력을 하면 변수로 취급한다.
fmt.Println(s, c, a4, s3, d2, m)
learnFlowControl() // 잠시 후에 다시 나옴
}
// Go는 가비지 컬렉션 기능을 JVM 같은 곳이 아닌 실행파일 런타임에 포함하고 있다.
// 그리고 포인터는 있지만, 포인터 연산(*p++ 같은)은 없다.
// 그래서 nil 포인터 접근같은 것 때문에 실수를 할 수는 있지만
// 포인터 연산으로 인한 실수는 없게 된다.
func learnMemory() (p, q *int) {
// 지명된 리턴 값(named return value)인 p와 q는 int에 대한 포인터 타입이다.
p = new(int) // 내장함수인 new는 메모리를 할당해준다.
// 메모리 할당된 int는 0으로 초기화 되고, p는 이제 nil이 아니다.
s := make([]int, 20) // 메모리의 단일 블록으로 20개의 int 공간을 할당한다.
s[3] = 7 // 그중 하나에 값을 준다.
r := -2 // 또다른 로컬 변수를 선언한다.
return &s[3], &r // &는 어떤 대상체의 메모리 주소를 가져오게 된다.
}
func expensiveComputation() int {
return 1e6
}
func learnFlowControl() {
// if문에 중괄호는 필요하지만, 조건이 들어갈 곳에 소괄호는 쓰지 않는다.
if true {
fmt.Println("told ya")
}
// 모든 Go 소스의 코드 포맷팅은 "go fmt" 커맨드라인 명령으로 소스코드의 포맷을 맞춘다.
if false {
// pout
} else {
// gloat
}
// if-else 체인 형태보다 switch 사용이 권장된다.
x := 1
switch x {
case 0:
case 1:
// case 안에서는 break가 없어도 자동으로 다음 case로 내려가지 않는다.
// 자동으로 내려가게 하려면 fallthrough 키워드를 사용한다.
case 2:
// x는 1이므로 여기는 실행되지 않음.
}
// if 에서처럼 for 에서도 양쪽에 소괄호를 쓰지 않는다.
for x := 0; x < 3; x++ { // ++ 은 실행을 제어하는 하나의 구문(statement)이다.
fmt.Println("iteration", x)
}
// 여기서 x는 1이다. 위 for에서 x는 for 안의 블록 범위에 있기 때문.
// For is the only loop statement in Go, but it has alternate forms.
// for 는 Go에서 유일한 루프 구문이지만 다양한 형태로 조건을 주거나 while
// 처럼 쓸 수도 있다.
for { // 무한루프
break // 여기서 곧바로 break를 한 건 단지
continue // break, continue를 루프 안에서 쓸 수 있다는 것을 보여주기 위함.
}
// for 에서처럼 if 에서 := 를 사용하는것은 y에 먼저 값을 대입하고,
// 그리고 y > x를 검사한다는 의미.
if y := expensiveComputation(); y > x {
x = y
}
// 함수 리터럴은 클로저다.
xBig := func() bool {
return x > 100 // 위 switch 문 바로 위에 있는 x를 참조한다.
}
fmt.Println("xBig:", xBig()) // true (x에 1e6를 대입했었다.)
x /= 1e5 // x는 10이 된다.
fmt.Println("xBig:", xBig()) // 이제 xBig()의 결과는 false가 된다.
// `goto`가 필요하다면, 좋아하게 될지도...
goto love
love:
learnDefer() // defer에 대해
learnInterfaces() // 곧이어서 좋은 기능에 대한 설명이 나올 거다.
}
func learnDefer() (ok bool) {
// deferred statements are executed just before the function returns.
// 연기된(deferred) 구문은 함수가 리턴하기 직전에 실행된다.
defer fmt.Println("deferred statements execute in reverse (LIFO) order.") // 연기된 구문은 LIFO순으로 실행된다.
defer fmt.Println("\nThis line is being printed first because") // 이 줄이 먼저 실행된다.
// defer는 주로 파일을 닫는데 사용된다.
// 파일을 닫는함수를 파일을 여는함수에 가까이 둘수 있다.
return true
}
// String 이라는 메서드 하나를 가진 Stringer 라는 인터페이스 타입을 정의하자.
type Stringer interface {
String() string
}
// x와 y라는 이름의 int 타입 필드를 가진 pair라는 struct를 정의하자.
type pair struct {
x, y int
}
// pair 타입에 메서드 String을 정의하자.
// 이제 pair는 Stringer 인터페이스를 구현(implement)한 것이 되었다.
func (p pair) String() string { // 여기서 p는 리시버(receiver)라고 부른다.
// Sprintf는 fmt 패키지 안에 있는 외부로 공개된(exported) 함수다.
// 점(.)으로 p의 필드들을 참조할 수 있다.
return fmt.Sprintf("(%d, %d)", p.x, p.y)
}
func learnInterfaces() {
// 중괄호 문법은 "구조체 리터럴(struct literal)"인데, 초기화된 구조체로
// 취급하게 해준다. := 문법으로 p를 이 구조체로 선언하고 초기화한다.
p := pair{3, 4}
fmt.Println(p.String()) // 타입 pair인 p의 String 메서드를 호출.
var i Stringer // Stringer 인터페이스 타입 i를 선언.
i = p // pair는 Stringer를 구현했기 때문에 이 대입은 유효하다.
// 타입 Stringer인 i의 String 메서드 호출. 결과는 위와 같다.
fmt.Println(i.String())
// fmt 패키지의 함수들을 통해 어떤 객체를 출력해보려고 할 때,
// fmt 패키지 내에서는 그 객체가 가진 String 메서드를 호출하도록 되어 있다.
fmt.Println(p) // 결과는 위와 같다. Println은 String 메서드를 호출한다.
fmt.Println(i) // 결과는 위와 같다.
learnVariadicParams("great", "learning", "here!")
}
// 함수는 가변 인수(variadic) 파라미터를 가질수 있다.
func learnVariadicParams(myStrings ...interface{}) {
// 가변 인수를 차례로 반복한다.
// 여기서 언더바(언더스코어, `_`)는 배열의 인덱스 인수를 무시한다.
// The underbar here is ignoring the index argument of the array.
for _, param := range myStrings {
fmt.Println("param:", param)
}
// 가변 인수 값을 가변인수 파라미터로 보내기.
fmt.Println("params:", fmt.Sprintln(myStrings...))
learnErrorHandling()
}
func learnErrorHandling() {
// ", ok" (comma okay)표현은 무언가가 맞는 것인지 아닌지 확인하는데 사용된다.
m := map[int]string{3: "three", 4: "four"}
if x, ok := m[1]; !ok { // 이 map 안에 키가 1인 것은 없으므로 ok는 false가 된다.
fmt.Println("no one there")
} else {
fmt.Print(x) // 만일 1이 map에 있었다면 x는 키 1의 값이 들어가게 된다.
}
// Go에서는 함수가 복수 개의 리턴 값을 줄 수 있다는 점을 활용해 함수의 두 번째 리턴
// 값으로 error를 리턴해주고 그 error가 nil 인지 아닌지 확인하는 관례가 있다.
// 이때 이 error 값은 단지 위에서처럼 함수의 결과가 성공했는지 실패했는지를 확인하는
// 것뿐만 아니라 실패 시 어떤 문제가 있었는지 확인할 수 있는 수단도 된다.
if _, err := strconv.Atoi("non-int"); err != nil { // _ 는 값을 안 쓰고 버린다는 의미.
// "strconv.ParseInt: parsing "non-int": invalid syntax" 이런 에러가 출력된다.
fmt.Println(err)
}
// 인터페이스에 대해 잠시 후에 다시 잠깐 볼 것이다.
learnConcurrency()
}
// c는 goroutine 간의 통신을 위한 채널(channel)이다.
func inc(i int, c chan int) {
c <- i + 1 // 채널이 <- 이 연산자 왼쪽에 온다면 그 채널로 데이터를 보낸다는 의미다.
}
// 우리는 어떤 숫자들을 동시에 증가시키기 위해 inc 함수를 사용할 것이다.
func learnConcurrency() {
// make는 slice, map, channel 타입들에 대해 메모리를 할당하고 초기화를 한다.
// Go에는 메모리 할당 방법으로 new와 make가 있다.
c := make(chan int)
// 3개의 동시에 실행되는 goroutine를 시작한다. 만약 실행하고 있는 머신이
// 멀티코어 CPU를 가지고 있고 올바르게 설정되어(GOMAXPROCS) 있다면
// 숫자가 정말로 병렬적으로 증가하게 될 것이다.
go inc(0, c) // go는 새로운 goroutine을 시작하는 구문이다.
go inc(10, c)
go inc(-805, c)
// 채널로부터 3개의 결과를 읽어 출력한다.
// 결과가 어떤 순서로 오는지는 알 수 없다.
fmt.Println(<-c, <-c, <-c) // 채널이 <- 연산자 오른쪽에 있는 건, 채널로부터 데이터를 받는 연산이다.
cs := make(chan string) // string을 다루는 또 다른 채널
cc := make(chan chan string) // string 채널의 채널
go func() { c <- 84 }() // c 채널로 값을 보내는 goroutine 시작.
go func() { cs <- "wordy" }() // cs 채널로 값을 보내느 goroutine 시작.
// select 구문은 switch 문과 비슷하지만, case에서 채널 연산에 관한 일을 한다.
// select의 case들은 채널통신을 할 준비가 된 case 하나가 무작위로 선택되어
// 그 부분이 실행된다.
select {
case i := <-c: // 채널로부터 받아진 값은 변수에 대입할 수 있다.
fmt.Printf("it's a %T", i)
case <-cs: // 또는 받은 값을 그냥 버릴 수도 있다.
fmt.Println("it's a string")
case <-cc: // 통신할 준비가 되어 있지 않은 비어있는 채널.
fmt.Println("didn't happen.")
}
// 여기서는 c나 cs 채널로부터 값 하나를 받을 수 있다. 위에서 실행한 두 개의
// goroutine 중 하나가 완료되면 다른 하나는 블락된 상태로 있게 된다.
learnWebProgramming() // Go에서는 웹 서버쪽 개발도 쉽게 할 수 있다.
}
// http 패키지의 함수 하나로 웹 서버를 실행시킨다.
func learnWebProgramming() {
// ListenAndServe의 첫 번째 파라미터는 listen 하기 위한 TCP 주소고,
// 두 번째 파라미터는 http.Handler 인터페이스다.
err := http.ListenAndServe(":8080", pair{})
fmt.Println(err) // don't ignore errors
}
// http.Handler의 하나 뿐인 메서드, ServeHTTP를 pair에서 구현한다.
func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// http.ResponseWriter의 메서드로 클라이언트에게 데이터를 보낸다.
w.Write([]byte("You learned Go in Y minutes!"))
}
```
## 더 읽어볼 것들
Go에 대한 모든 것들은 [Go 공식 웹 사이트](https://go.dev/)를 참고하자.
여기에는 따라해볼 튜토리얼, 웹 기반의 인터랙티브 실행환경과 많은 읽을거리들이 있다.
Go 언어 자체에 대한 스펙도 읽어보기를 적극 추천한다. 읽기 쉽게 되어있고
그리 길지는 않다.
Go 소스코드에 대해 좀더 알아보고 싶다면 [Go 표준 라이브러리](https://go.dev/src/)를
분석해보기 바란다. 이해하기 쉽게 문서화되어 있고, Go 스타일 그리고 Go에서의
관례 배우기에 가장 좋은 방법일 것이다. 또는 [문서](https://go.dev/pkg/) 안에서
함수 이름 하나를 클릭해보면 소스코드를 브라우저에서 살펴볼 수도 있다.
Go를 배울수 있는 또하나의 좋은 방법은 [Go by example](https://gobyexample.com/).

407
ko/java.md Normal file
View File

@@ -0,0 +1,407 @@
---
language: Java
filename: java-kr.java
category: language
contributors:
- ["Jake Prather", "http://github.com/JakeHP"]
translators:
- ["wikibook", "http://wikibook.co.kr"]
lang: ko-kr
---
자바는 일반 목적으로 사용할 수 있고 동시성을 지원하며, 클래스 기반의 객체지향 컴퓨터 프로그래밍 언어입니다.
[더 자세한 사항](http://docs.oracle.com/javase/tutorial/java/index.html)
```java
// 한 줄짜리 주석은 //로 시작합니다.
/*
여러 줄 주석은 다음과 같은 형태입니다.
*/
/**
자바독(JavaDoc) 주석은 이렇게 생겼습니다. 자바독 주석은 클래스나 클래스의
다양한 속성을 기술하는 데 사용됩니다.
*/
// java.util 패키지 안에 있는 ArrayList 클래스를 임포트합니다.
import java.util.ArrayList;
// java.security 패키지 안에 있는 모든 클래스를 임포트합니다.
import java.security.*;
// 각 .java 파일에는 공용(public) 클래스가 들어 있으며, 클래스의 이름은
// 파일명과 동일합니다.
public class LearnJava {
// 프로그램에는 반드시 진입점 역할을 하는 main 메서드가 하나 있어야 합니다.
public static void main (String[] args) {
// System.out.println을 이용해 한 줄을 출력합니다.
System.out.println("Hello World!");
System.out.println(
"Integer: " + 10 +
" Double: " + 3.14 +
" Boolean: " + true);
// 줄바꿈 없이 뭔가를 출력하려면 System.out.print를 사용합니다.
System.out.print("Hello ");
System.out.print("World");
///////////////////////////////////////
// 타입 & 변수
///////////////////////////////////////
// <타입> <이름>과 같은 형태로 변수를 선언합니다.
// Byte - 부호가 있는 8비트 2의 보수 정수
// (-128 <= byte <= 127)
byte fooByte = 100;
// Short - 부호가 있는 16비트 2의 보수 정수
// (-32,768 <= short <= 32,767)
short fooShort = 10000;
// Integer - 부호가 있는 32비트 2의 보수 정수
// (-2,147,483,648 <= int <= 2,147,483,647)
int fooInt = 1;
// Long - 부호가 있는 64비트 2의 보수 정수
// (-9,223,372,036,854,775,808 <= long <= 9,223,372,036,854,775,807)
long fooLong = 100000L;
// L은 이 변수의 값이 Long 타입임을 나타내는 데 사용됩니다.
// L이 없는 것들은 기본적으로 정수로 간주됩니다.
// 참고: 자바에는 부호 없는(unsigned) 타입이 없습니다.
// Float - 단정도 32비트 IEEE 754 부동 소수점 수
float fooFloat = 234.5f;
// f는 이 변수의 값이 float 타입임을 나타내는 데 사용됩니다.
// f를 지정하지 않으면 double로 간주됩니다.
// Double - 배정도 64비트 IEEE 754 부동 소수점 수
double fooDouble = 123.4;
// Boolean - 참(true) & 거짓(false)
boolean fooBoolean = true;
boolean barBoolean = false;
// Char - 단일 16비트 유니코드 문자
char fooChar = 'A';
// 변수를 변경할 수 없게 만들려면 final을 지정합니다.
final int HOURS_I_WORK_PER_WEEK = 9001;
// 문자열
String fooString = "My String Is Here!";
// \n은 새로운 줄을 시작하는 이스케이프 문자입니다.
String barString = "Printing on a new line?\nNo Problem!";
// \t는 탭 문자를 추가하는 이스케이프 문자입니다.
String bazString = "Do you want to add a tab?\tNo Problem!";
System.out.println(fooString);
System.out.println(barString);
System.out.println(bazString);
// 배열
// 배열의 크기는 반드시 선언할 때 결정해야 합니다.
// 배열을 선언하는 형식은 다음과 같습니다.
//<자료형> [] <변수명> = new <자료형>[<배열 크기>];
int [] intArray = new int[10];
String [] stringArray = new String[1];
boolean [] booleanArray = new boolean[100];
// 배열을 선언하고 초기화하는 또 다른 방법
int [] y = {9000, 1000, 1337};
// 배열 인덱스 - 요소에 접근
System.out.println("intArray @ 0: " + intArray[0]);
// 배열의 인덱스는 0에서부터 시작하며 변경 가능합니다.
intArray[1] = 1;
System.out.println("intArray @ 1: " + intArray[1]); // => 1
// 기타 참고할 만한 자료구조
// ArrayLists - 좀 더 많은 기능을 제공하고 크기를 변경 가능하다는 점을
// 제외하면 배열과 비슷합니다.
// LinkedLists
// Maps
// HashMaps
///////////////////////////////////////
// 연산자
///////////////////////////////////////
System.out.println("\n->Operators");
int i1 = 1, i2 = 2; // 다중 선언의 축약형
// 산술 연산은 이해하기 어렵지 않습니다.
System.out.println("1+2 = " + (i1 + i2)); // => 3
System.out.println("2-1 = " + (i2 - i1)); // => 1
System.out.println("2*1 = " + (i2 * i1)); // => 2
System.out.println("1/2 = " + (i1 / i2)); // => 0 (0.5를 잘라 버립니다)
// 나눗셈
System.out.println("11%3 = "+(11 % 3)); // => 2
// 비교 연산자
System.out.println("3 == 2? " + (3 == 2)); // => false
System.out.println("3 != 2? " + (3 != 2)); // => true
System.out.println("3 > 2? " + (3 > 2)); // => true
System.out.println("3 < 2? " + (3 < 2)); // => false
System.out.println("2 <= 2? " + (2 <= 2)); // => true
System.out.println("2 >= 2? " + (2 >= 2)); // => true
// 비트 연산자!
/*
~ 단항 보수 연산
<< 산술적 왼쪽 시프트
>> 산술적 오른쪽 시프트
>>> 논리적 오른쪽 시프트
& 비트 단위 논리곱(AND)
^ 비트 단위 배타적 논리합(OR)
| 비트 단위 논리합(OR)
*/
// 증감 연산자
int i = 0;
System.out.println("\n->Inc/Dec-rementation");
System.out.println(i++); //i = 1. 후치 증가 연산
System.out.println(++i); //i = 2. 전치 증가 연산
System.out.println(i--); //i = 1. 후치 감소 연산
System.out.println(--i); //i = 0. 전치 감소 연산
///////////////////////////////////////
// 제어 구조
///////////////////////////////////////
System.out.println("\n->Control Structures");
// if 문은 C 언어와 비슷합니다.
int j = 10;
if (j == 10){
System.out.println("I get printed");
} else if (j > 10) {
System.out.println("I don't");
} else {
System.out.println("I also don't");
}
// while 루프
int fooWhile = 0;
while(fooWhile < 100)
{
// System.out.println(fooWhile);
// 카운터를 증가
// 99번 반복, fooWhile 0->99
fooWhile++;
}
System.out.println("fooWhile Value: " + fooWhile);
// do-while 루프
int fooDoWhile = 0;
do
{
// System.out.println(fooDoWhile);
// 카운터를 증가
// 99번 반복, fooDoWhile 0->99
fooDoWhile++;
}while(fooDoWhile < 100);
System.out.println("fooDoWhile Value: " + fooDoWhile);
// for 루프
int fooFor;
// for 루프 구조 => for(<초기식>; <조건식>; <증감식>)
for(fooFor=0; fooFor<10; fooFor++){
// System.out.println(fooFor);
// 10번 반복, fooFor 0->9
}
System.out.println("fooFor Value: " + fooFor);
// switch-case 문
// switch는 byte, short, char, int 자료형을 대상으로 동작합니다.
// 아울러 열거형을 비롯해 String 클래스 및 원시 타입을 감싼 Character,
// Byte, Short, Integer와 같은 몇 가지 특별한 클래스에 대해서도 동작합니다.
int month = 3;
String monthString;
switch (month){
case 1:
monthString = "January";
break;
case 2:
monthString = "February";
break;
case 3:
monthString = "March";
break;
default:
monthString = "Some other month";
break;
}
System.out.println("Switch Case Result: " + monthString);
///////////////////////////////////////
// 자료형 변환과 형변환
///////////////////////////////////////
// 데이터 변환
// 문자열에서 정수로 변환
Integer.parseInt("123");// 정수 버전의 "123"을 반환
// 정수를 문자열로 변환
Integer.toString(123);// 문자열 버전의 123을 반환
// 다른 변환에 대해서는 아래 클래스를 확인해 보세요.
// Double
// Long
// String
// 형변환
// 자바 객체 또한 형변환할 수 있으며, 이와 관련해서 알아야 할 세부사항이
// 많을뿐더러 다소 중급 수준에 해당하는 개념들도 다뤄야 합니다.
// 이와 관련된 사항은 아래 링크를 참고하세요.
// http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html
///////////////////////////////////////
// 클래스와 함수
///////////////////////////////////////
System.out.println("\n->Classes & Functions");
// (Bicycle 클래스의 정의)
// 클래스를 인스턴스화하려면 new를 사용합니다.
Bicycle trek = new Bicycle();
// 객체의 메서드를 호출합니다.
trek.speedUp(3); // 항상 설정자 메서드와 접근자 메서드를 사용해야 합니다.
trek.setCadence(100);
// 현재 객체의 값을 표시할 때는 관례적으로 toString을 사용합니다.
System.out.println("trek info: " + trek.toString());
} // main 메서드 끝
} // LearnJava 클래스 끝
// .java 파일 안에 다른 비공개 클래스를 포함할 수 있습니다.
// 클래스 선언 문법:
// <public/private/protected> class <클래스명>{
// // 데이터 필드, 생성자, 함수가 모두 이곳에 들어갑니다.
// // 자바에서는 함수를 메서드라고 부릅니다.
// }
class Bicycle {
// Bicycle의 필드와 변수
public int cadence; // Public: 어느 곳에서도 접근할 수 있습니다.
private int speed; // Private: 클래스 안에서만 접근할 수 있습니다.
protected int gear; // Protected: 현재 클래스와 하위 클래스에서 접근할 수 있습니다.
String name; // default: 현재 패키지 안에서만 접근할 수 있습니다.
// 생성자는 클래스를 생성하는 방법 중 하나입니다.
// 다음은 기본 생성자입니다.
public Bicycle() {
gear = 1;
cadence = 50;
speed = 5;
name = "Bontrager";
}
// 다음은 구체화된 생성자입니다(인자를 담고 있습니다)
public Bicycle(int startCadence, int startSpeed, int startGear, String name) {
this.gear = startGear;
this.cadence = startCadence;
this.speed = startSpeed;
this.name = name;
}
// 함수 문법:
// <public/private/protected> <반환형> <함수명>(<인자>)
// 자바 클래스는 필드에 대해 접근자 메서드와 설정자 메서드를 구현할 때가 많습니다.
// 메서드 선언 문법:
// <유효범위> <반환형> <메서드명>(<인자>)
public int getCadence() {
return cadence;
}
// void 메서드는 반환형이 필요하지 않습니다.
public void setCadence(int newValue) {
cadence = newValue;
}
public void setGear(int newValue) {
gear = newValue;
}
public void speedUp(int increment) {
speed += increment;
}
public void slowDown(int decrement) {
speed -= decrement;
}
public void setName(String newName) {
name = newName;
}
public String getName() {
return name;
}
// 현재 객체의 속성값을 표시하는 메서드
@Override
public String toString() {
return "gear: " + gear +
" cadence: " + cadence +
" speed: " + speed +
" name: " + name;
}
} // Bicycle 클래스의 끝
// PennyFarthing은 Bicycle의 하위 클래스입니다.
class PennyFarthing extends Bicycle {
// (페니 파딩은 앞바퀴가 굉장히 큰 자전거입니다. 기어가 없죠.)
public PennyFarthing(int startCadence, int startSpeed){
// super를 이용해 부모 생성자를 호출합니다.
super(startCadence, startSpeed, 0, "PennyFarthing");
}
// @annotation을 이용해 재정의하는 메서드를 표시해야 합니다.
// 애노테이션과 애노테이션의 용도에 관한 자세한 내용은 아래 링크를 참고하세요.
// 애노테이션: http://docs.oracle.com/javase/tutorial/java/annotations/
@Override
public void setGear(int gear) {
gear = 0;
}
}
```
## 기타 참고자료
다음 링크를 통해 다양한 주제를 이해하고 구글을 통해 구체적인 예제들을 찾아보세요.
공부할 만한 기타 주제:
* [썬/오라클의 자바 자습서](http://docs.oracle.com/javase/tutorial/index.html)
* [자바 접근 제한자](http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html)
* [객체 지향 프로그래밍 개념](http://docs.oracle.com/javase/tutorial/java/concepts/index.html):
* [상속(Inheritance)](http://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html)
* [다형성(Polymorphism)](http://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html)
* [추상화(Abstraction)](http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html)
* [예외(Exceptions)](http://docs.oracle.com/javase/tutorial/essential/exceptions/index.html)
* [인터페이스(Interfaces)](http://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html)
* [제네릭(Generics)](http://docs.oracle.com/javase/tutorial/java/generics/index.html)
* [자바 코딩 관례(Java Code Conventions)](http://www.oracle.com/technetwork/java/codeconv-138413.html)

430
ko/javascript.md Normal file
View File

@@ -0,0 +1,430 @@
---
language: JavaScript
category: language
contributors:
- ["Leigh Brenecki", "https://leigh.net.au"]
translators:
- ["wikibook", "http://wikibook.co.kr"]
filename: javascript-kr.js
lang: ko-kr
---
자바스크립트는 넷스케이프의 브렌던 아이크(Brendan Eich)가 1995년에 만들었습니다.
원래 자바스크립트는 웹사이트를 위한 단순한 스크립트 언어를 목표로 만들어졌는데,
좀 더 복잡한 웹 애플리케이션을 만들기 위해 자바를 보완하는 역할이었지만
웹 페이지와의 긴밀한 상호작용과 브라우저에 대한 지원 기능 덕분에 웹 프론트엔드에서
자바보다 훨씬 더 보편적으로 쓰이게 됐습니다.
그렇지만 자바스크립트는 웹 브라우저에만 국한되지 않습니다. 구글 크롬의 V8 자바스크립트
엔진을 위한 독립형 런타임을 제공하는 Node.js는 점점 인기를 얻고 있습니다.
```js
// 주석은 C와 비슷합니다. 한 줄짜리 주석은 두 개의 슬래시로 시작하고,
/* 여러 줄 주석은 슬래시 별표로 시작해서
별표 슬래시로 끝납니다. */
// 구문은 세미콜론(;)으로 끝낼 수 있습니다.
doStuff();
// 하지만 꼭 그럴 필요는 없는데, 특정 경우를 제외하고
// 새 줄이 시작할 때마다 세미콜론이 자동으로 삽입되기 때문입니다.
doStuff()
// 여기서는 세미콜론을 생략하겠습니다. 세미콜론을 생략할지 여부는
// 개인적인 취향이나 프로젝트의 스타일 가이드를 따릅니다.
///////////////////////////////////
// 1. 숫자, 문자열, 연산자
// 자바스크립트에는 단 하나의 숫자 타입(64비트 IEEE 754 배정도 숫자)만이
// 있습니다.
3 // = 3
1.5 // = 1.5
// 모든 기초 산술 연산은 기대한 대로 동작합니다.
1 + 1 // = 2
8 - 1 // = 7
10 * 2 // = 20
35 / 5 // = 7
// 나누어 떨어지지 않는 나눗셈도 포함됩니다.
5 / 2 // = 2.5
// 비트 연산도 지원됩니다. float을 대상으로 비트 연산을 수행하면
// 32비트까지 부호가 있는 int로 변환됩니다.
1 << 2 // = 4
// 괄호를 이용하면 우선순위를 지정할 수 있습니다.
(1 + 3) * 2 // = 8
// 실제 숫자가 아닌 특별한 세 가지 값이 있습니다.
Infinity // 1/0 1/0과 같은 연산의 결과
-Infinity // -1/0과 같은 연산의 결과
NaN // 0/0과 같은 연산의 결과
// 불린 타입도 있습니다.
true
false
// 문자열은 '나 "로 생성합니다.
'abc'
"Hello, world"
// 부정 연산에는 ! 기호를 이용합니다.
!true // = false
!false // = true
// 동일성 연산은 ==
1 == 1 // = true
2 == 1 // = false
// 불일치 연산은 !=
1 != 1 // = false
2 != 1 // = true
// 그 밖의 비교 연산
1 < 10 // = true
1 > 10 // = false
2 <= 2 // = true
2 >= 2 // = true
// 문자열은 +로 연결할 수 있습니다.
"Hello " + "world!" // = "Hello world!"
// 그리고 <와 >로 비교할 수 있습니다.
"a" < "b" // = true
// 비교 시 타입 강제변환이 수행됩니다.
"5" == 5 // = true
// ===를 쓰지 않는다면 말이죠.
"5" === 5 // = false
// charAt을 이용하면 문자열 내의 문자에 접근할 수 있습니다.
"This is a string".charAt(0)
// null과 undefined도 있습니다.
null // 의도적으로 값이 아님을 나타내는 데 사용합니다.
undefined // 값이 아직 설정되지 않음을 나타내는 데 사용합니다.
// null, undefinded, NaN, 0, ""은 거짓이며, 그 밖의 다른 모든 값은 참입니다.
// 참고로 0은 거짓이며, "0"은 참입니다(심지어 0 == "0"이더라도).
///////////////////////////////////
// 2. 변수, 배열, 객체
// 변수는 var 키워드로 선언합니다. 자바스크립트는 동적 타입 언어라서
// 타입을 지정할 필요가 없습니다. 값을 할당할 때는 = 문자 하나를 사용합니다.
var someVar = 5
// var 키워드를 지정하지 않아도 오류는 발생하지 않습니다.
someOtherVar = 10
// 그렇지만 변수가 여러분이 정의한 유효범위가 아니라
// 전역 유효범위에 생성됩니다.
// 값을 할당하지 않은 채로 선언한 변수는 undefined로 설정됩니다.
var someThirdVar // = undefined
// 변수에 수학 연산을 수행하는 축약형 표현은 다음과 같습니다.
someVar += 5 // someVar = someVar + 5;와 같음. 이제 someVar는 10.
someVar *= 10 // somVar는 100
// 1을 더하거나 빼는 훨씬 더 짧은 표현도 있습니다.
someVar++ // 이제 someVar는 101
someVar-- // 다시 100으로 되돌아감
// 배열은 순차적인 임의 타입 값의 목록입니다.
var myArray = ["Hello", 45, true]
// 배열의 멤버는 대괄호로 둘러싼 인덱스를 이용해 접근할 수 있습니다.
// 배열의 인덱스는 0부터 시작합니다.
myArray[1] // = 45
// 자바스크립트의 객체는 다른 언어의 '사전'이나 '맵'과 같습니다.
// 즉, 키-값 쌍으로 구성된 비순차 컬렉션입니다.
{key1: "Hello", key2: "World"}
// 키는 문자열이지만 유효한 자바스크립트 식별자일 경우
// 작은따옴표는 필요하지 않습니다. 값은 어떤 타입이든 사용할 수 있습니다.
var myObj = {myKey: "myValue", "my other key": 4}
// 객체 속성에도 인덱스를 이용해 접근할 수 있습니다.
myObj["my other key"] // = 4
// 또는 키가 유효한 식별자일 경우 점 표기법을 이용해 접근할 수 있습니다.
myObj.myKey // = "myValue"
// 객체는 변경 가능합니다. 즉, 값을 변경하거나 새 키를 추가할 수 있습니다.
myObj.myThirdKey = true
// 설정되지 않은 값에 접근하려고 하면 undefined가 반환됩니다.
myObj.myFourthKey // = undefined
///////////////////////////////////
// 3. 로직과 제어 구조
// if 구조는 여러분이 예상한 대로 동작합니다.
var count = 1
if (count == 3){
// count가 3일 경우 평가됨
} else if (count == 4) {
// count가 4일 경우 평가됨
} else {
// count가 3이나 4가 아닌 경우에 평가됨
}
// while도 마찬가지입니다.
while (true) {
// 무한 루프!
}
// do-while 문은 항상 최소 한 번은 실행된다는 점을 제외하면
// while 문과 비슷합니다.
var input
do {
input = getInput()
} while (!isValid(input))
// for 문은 C와 자바의 for 문과 같습니다.
// 초기화식; 지속 조건; 증감식
for (var i = 0; i < 5; i++){
// 5번 실행됨
}
// &&는 논리 and이고 ||는 논리 or입니다.
if (house.size == "big" && house.colour == "blue"){
house.contains = "bear"
}
if (colour == "red" || colour == "blue"){
// 색은 빨강이거나 파랑
}
// &&와 ||은 "단축 평가"를 수행하는데, 기본값을 설정할 때 유용합니다.
var name = otherName || "default"
///////////////////////////////////
// 4. 함수, 유효범위, 클로저
// 자바스크립트 함수는 function 키워드로 선언합니다.
function myFunction(thing){
return thing.toUpperCase()
}
myFunction("foo") // = "FOO"
// 함수는 "익명"으로, 즉 이름 없이 정의할 수도 있습니다.
function(thing){
return thing.toLowerCase()
}
// (함수를 가리키는 이름이 없기 때문에 함수를 호출할 수 없습니다)
// 자바스크립트 함수는 일급 객체이므로 다른 변수에 재할당하고
// 다른 함수에 인자로 전달할 수 있습니다. 가령, 이벤트 핸들러를 만들 경우
function myFunction(){
// 이 코드는 5초 내에 호출됨
}
setTimeout(myFunction, 5000)
// 다른 함수를 호출할 때 직접적으로 함수 구문을 작성할 수도 있습니다.
setTimeout(function myFunction(){
// 이 코드는 5초 내에 호출됨
}, 5000)
// 자바스크립트에는 함수 유효범위가 있습니다.
// 함수는 자체적인 유효범위를 가지지만 다른 블록은 유효범위를 가지지 않습니다.
if (true){
var i = 5
}
i // = 5 - 블록 유효범위를 지원하는 언어에서는 undefined가 아닙니다.
// 이것은 "즉시 실행되는 익명 함수"라는 공통 패턴으로 이어지는데,
// 이 패턴은 임시 변수가 전역 유효범위로 유출되는 것을 방지합니다.
(function(){
var temporary = 5
// '전역 객체'에 할당하는 식으로 전역 유효범위에 접근할 수 있는데,
// 브라우저에서 전역 객체는 항상 'window'입니다. 전역 객체는
// Node.js와 같은 브라우저가 아닌 환경에서는 다른 이름일 수도 있습니다.
window.permanent = 10
// 또는 앞에서 언급했다시피 var 키워드를 뺄 수도 있습니다.
permanent2 = 15
})()
temporary // ReferenceError 발생
permanent // = 10
permanent2 // = 15
// 자바스크립트의 강력한 기능 중 하나는 클로저(closure)입니다.
// 함수가 다른 함수 안에서 정의되면 안쪽에 정의된 함수는 바깥 함수의
// 모든 변수에 접근할 수 있습니다.
function sayHelloInFiveSeconds(name){
var prompt = "Hello, " + name + "!"
function inner(){
alert(prompt)
}
setTimeout(inner, 5000)
// setTimeout은 비동기적으로 동작하므로 이 함수는 5초 동안
// 기다리지 않고 실행을 마칩니다. 하지만 5초가 지나면 inner에서도
// prompt의 값에 접근할 수 있습니다.
}
sayHelloInFiveSeconds("Adam") // 5초 내로 "Hello, Adam!"이라고 적힌 팝업이 표시됨
///////////////////////////////////
// 5. 객체 심화; 생성자와 프로토타입
// 객체는 함수를 포함할 수 있습니다.
var myObj = {
myFunc: function(){
return "Hello world!"
}
}
myObj.myFunc() // = "Hello world!"
// 객체에 포함된 함수가 호출되면 함수에서는 this 키워드를 이용해
// 해당 함수가 포함된 객체에 접근할 수 있습니다.
myObj = {
myString: "Hello world!",
myFunc: function(){
return this.myString
}
}
myObj.myFunc() // = "Hello world!"
// 여기서 설정한 것은 함수가 정의된 곳이 아닌 함수가 호출되는
// 방식과 관련이 있습니다. 그래서 아래 함수는 객체 컨텍스트에서
// 호출되지 않으면 동작하지 않습니다.
var myFunc = myObj.myFunc
myFunc() // = undefined
// 반대로 함수는 객체에 할당하고 this를 통해 해당 객체에 접근할 수 있습니다.
// 함수를 정의할 때 객체에 추가되지 않았더라도 마찬가지입니다.
var myOtherFunc = function(){
return this.myString.toUpperCase()
}
myObj.myOtherFunc = myOtherFunc
myObj.myOtherFunc() // = "HELLO WORLD!"
// new 키워드로 함수를 호출하면 새로운 객체가 생성되고 this를 통해
// 함수에서 사용할 수 있게 됩니다. 이런 식으로 설계된 함수를 생성자라 합니다.
var MyConstructor = function(){
this.myNumber = 5
}
myNewObj = new MyConstructor() // = {myNumber: 5}
myNewObj.myNumber // = 5
// 모든 자바스크립트 객체는 'prototype'을 가지고 있습니다. 어떤 객체에 대해
// 실제 객체에는 존재하지 않는 프로퍼티에 접근하면 인터프리터는 프로로타입에서
// 해당 프로퍼티를 찾습니다.
// 일부 자바스크립트 구현체에서는 __proto__라는 마법의 프로퍼티로
// 객체의 프로토타입에 접근하는 것을 허용하기도 합니다. 프로토타입을
// 설명하기에는 이런 내용도 도움되겠지만 __proto__는 표준에 포함돼
// 있지 않습니다. 나중에 프로토타입을 사용하는 표준 방법을 살펴보겠습니다.
var myObj = {
myString: "Hello world!",
}
var myPrototype = {
meaningOfLife: 42,
myFunc: function(){
return this.myString.toLowerCase()
}
}
myObj.__proto__ = myPrototype
myObj.meaningOfLife // = 42
// 이 방법은 함수에도 통합니다.
myObj.myFunc() // = "hello world!"
// 물론 프로퍼티가 프로토타입에 존재하지 않으면
// 프로토타입의 프로토타입을 찾는 식으로 진행됩니다.
myPrototype.__proto__ = {
myBoolean: true
}
myObj.myBoolean // = true
// 여기서 복사는 일어나지 않습니다. 각 객체에는 프로토타입에 대한
// 참조가 보관돼 있습니다. 이는 프로토타입을 변경하면 변경사항이
// 모든 곳에 반영된다는 의미입니다.
myPrototype.meaningOfLife = 43
myObj.meaningOfLife // = 43
// 앞에서 __proto__가 표준에 포함돼 있지 않다고 이야기했는데,
// 기존 객체의 프로토타입을 변경하는 표준 방법은 없습니다.
// 하지만 특정 프로토타입을 가지고 새로운 객체를 생성하는 두 가지
// 방법이 있습니다.
// 첫 번째 방법은 Object.create를 이용하는 것인데,
// Object.create는 최근에 자바스크립트에 추가된 것이라서 아직까지
// 모든 구현체에서 이용할 수 있는 것은 아닙니다.
var myObj = Object.create(myPrototype)
myObj.meaningOfLife // = 43
// 두 번째 방법은 어디서나 통하는 방법인데, 생성자와 관련이 있습니다.
// 생성자에는 prototype이라는 프로퍼티가 있습니다. 이 프로퍼티는
// 생성자 함수 자체의 프로토타입이 *아니고* 생성자와 new 키워드를 이용해
// 객체가 생성될 때 새로운 객체가 받는 프로토타입입니다.
myConstructor.prototype = {
getMyNumber: function(){
return this.myNumber
}
}
var myNewObj2 = new myConstructor()
myNewObj2.getMyNumber() // = 5
// 문자열과 숫자와 같은 내장 타입에도 동등한 래퍼 객체를
// 생성하는 생성자가 있습니다.
var myNumber = 12
var myNumberObj = new Number(12)
myNumber == myNumberObj // = true
// 하지만 정확히 같지는 않습니다.
typeof myNumber // = 'number'
typeof myNumberObj // = 'object'
myNumber === myNumberObj // = false
if (0){
// 0은 거짓이라서 이 코드는 실행되지 않습니다.
}
// 하지만 래퍼 객체와 일반 내장 함수는 프로토타입을 공유하기 때문에
// 가령 문자열에 실제로 기능을 추가할 수 있습니다.
String.prototype.firstCharacter = function(){
return this.charAt(0)
}
"abc".firstCharacter() // = "a"
// 이러한 사실은 기존 자바스크립트 버전에서 자바스크립트의
// 새로운 기능을 구현하는 "폴리필(polyfilling)"에 자주 이용되므로
// 오래된 버전의 브라우저와 같이 기존 환경에서 사용될 수 있습니다.
// 예를 들어, Object.create가 모든 구현체에서 사용 가능한 것은 아니라고
// 했지만 아래의 폴리필을 이용해 Object.create를 여전히 사용할 수 있습니다.
if (Object.create === undefined){ // 이미 존재하면 덮어쓰지 않음
Object.create = function(proto){
// 올바른 프로토타입을 가지고 임시 생성자를 만듬
var Constructor = function(){}
Constructor.prototype = proto
// 그런 다음 임시 생성자를 이용해 새로운 적절한 프로토타입을
// 포함한 객체를 생성
return new Constructor()
}
}
```
## 기타 참고 자료
[모질라 개발자 네트워크](https://developer.mozilla.org/en-US/docs/Web/JavaScript)에서는
자바스크립트에 대한 훌륭한 문서를 제공합니다. 더불어 위키 형식이라서 좀 더 많은 사항을
배우게 되면 여러분만의 지식을 공유함으로써 다른 사람들에게 도움을 줄 수도 있습니다.
MDN의 ['자바스크립트 재입문'](https://developer.mozilla.org/ko/docs/A_re-introduction_to_JavaScript)에서는
여기서 다룬 개념의 상당수를 더욱 자세히 다루고 있습니다. 이 자료에서는 자바스크립트 언어 자체에
대해서만 상당히 신중하게 다뤘습니다. 웹 페이지에서 자바스크립트를 사용하는 방법을 배우고 싶다면
[문서 객체 모델(Document Object Model)](https://developer.mozilla.org/en-US/docs/Using_the_W3C_DOM_Level_1_Core)에
관해 배우는 것으로 시작하길 바랍니다.
[자바스크립트 가든](https://shamansir.github.io/JavaScript-Garden/)에서는 자바스크립트 언어에서
직관에 어긋나는 모든 부분들을 심도 있게 다룹니다.
더불어 이 글에 직접적으로 기여한 분들로, 내용 중 일부는 이 사이트에 있는
루이 딘(Louie Dihn)의 파이썬 튜토리얼과 모질라 개발자 네트워크에 있는
[자바스크립트 튜토리얼](https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript)을 참고했습니다.

80
ko/json.md Normal file
View File

@@ -0,0 +1,80 @@
---
language: JSON
filename: learnjson-kr.json
contributors:
- ["Anna Harren", "https://github.com/iirelu"]
- ["Marco Scannadinari", "https://github.com/marcoms"]
- ["himanshu", "https://github.com/himanshu81494"]
- ["Michael Neth", "https://github.com/infernocloud"]
translators:
- ["Wooseop Kim", "https://github.com/linterpreteur"]
lang: ko-kr
---
JSON은 아주 간단한 데이터 교환 포맷입니다. [json.org](http://json.org/json-ko.html)에 의하면, 사람이 읽고 쓰기 쉬우며 기계가 분석하고 생성하기 쉽습니다.
JSON 한 개는 반드시 이하의 둘 중 하나를 나타내야 합니다.
* 이름과 값 쌍의 모임(`{ }`). 이는 다양한 언어에서 객체, 레코드, 구조체, 딕셔너리, 해시 테이블, 키 리스트, 혹은 연관 배열로 구현됩니다.
* 값에 순서가 있는 리스트 (`[ ]`). 이는 다양한 언어에서 배열, 벡터, 리스트, 시퀀스로 구현됩니다.
순수한 JSON은 사실 주석이 없지만 대부분의 파서는 C 스타일의 주석(`//`, `/* */`)도 받아들일 겁니다. 일부 파서는 꼬리에 오는 쉼표, 즉 배열의 마지막 원소 혹은 객체의 마지막 속성 다음에 오는 쉼표도 인정하겠지만, 호환성을 위해 쓰지 않는 것이 좋습니다.
이 튜토리얼의 목적에 따라 모든 것은 100% 유효한 JSON입니다. 다행스럽게도 JSON은 다소 자기서술적입니다.
지원하는 데이터 형:
* 문자열: `"안녕"`, `"\"따옴표.\""`, `"\u0abe"`, `"개행 문자.\n"`
* 수: `23`, `0.11`, `12e10`, `3.141e-10`, `1.23e+4`
* 객체: `{ "키": "값" }`
* 배열: `["값 값 값"]`
* 기타: `true`, `false`, `null`
```json
{
"키": "값",
"키는": "반드시 큰따옴표 안에 있어야 합니다.",
"수": 0,
"문자열": "Hellø, wørld. 모든 유니코드와 \"탈출 문자\"가 지원됩니다.",
"부울도 있나?": true,
"아무 것도 없는 건": null,
"큰 수": 1.2e+100,
"객체": {
"주석": "문서 구조의 대부분은 객체가 될 것입니다.",
"배열": [0, 1, 2, 3, "배열 안에는 무엇이든 올 수 있습니다.", 5],
"다른 객체": {
"주석": "객체는 객체를 포함할 수 있습니다. 아주 유용하죠."
}
},
"장난이지롱": [
{
"칼륨이 풍부한": ["바나나"]
},
[
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, "neo"],
[0, 0, 0, 1]
]
],
"다른 방식": {
"주석": "여기 보세요!"
, "쉼표의 위치는": "상관 없습니다. 다음 키 전에만 온다면 유효합니다."
, "다른 주석": "참 좋죠"
},
"공백은": "상관이 없습니다.",
"짧았죠": "끝입니다. JSON의 모든 것을 터득하셨습니다."
}
```
## 더 읽기
* [JSON.org](http://json.org/json-ko.html) 플로우차트와 같은 그래픽을 이용해 설명한 JSON의 모든 것.

375
ko/kotlin.md Normal file
View File

@@ -0,0 +1,375 @@
---
language: Kotlin
contributors:
- ["S Webber", "https://github.com/s-webber"]
translators:
- ["Alan Jeon", "https://github.com/skyisle"]
lang: ko-kr
filename: LearnKotlin-kr.kt
---
Kotlin 은 정적 타입 프로그래밍 언어로 JVM, 안드로이드, 브라우져를 지원하며 Java 와 100% 상호 운용이 가능합니다.
[자세한 내용은 다음을 참고하세요.](https://kotlinlang.org/)
```kotlin
// 한 줄짜리 주석은 // 로 시작합니다.
/*
여러 줄 주석은 이와 같이 표시합니다.
*/
// "package" 예약어는 자바와 동일하게 사용됩니다.
package com.learnxinyminutes.kotlin
/*
Kotlin 프로그램의 진입점은 main 이라는 함수명으로 지정됩니다.
이 함수에 명령행 인수가 배열로 전달됩니다.
*/
fun main(args: Array<String>) {
/*
값을 선언할때는 "var" 또는 "val"이 사용됩니다.
"var"와는 다르게 "val"로 선언된 변수에는 값을 재할당 할 수 없습니다.
*/
val fooVal = 10 // fooVal 에 다른 값을 다시 할당 할 수 없습니다.
var fooVar = 10
fooVar = 20 // fooVar 에는 선언 이후에도 다른 값을 할당 할 수 있습니다
/*
대부분의 경우, Kotlin 에서는 변수 타입을 판단할 수 있기때문에 명시적으로 지정해 주지 않을 수 있습니다.
다음과 같이 변수의 타입을 명시적으로 지정할 수 있습니다.
*/
val foo: Int = 7
/*
문자형은 Java와 비슷하게 표시될 수 있습니다.
이스케이핑에는 백슬래시를 사용합니다.
*/
val fooString = "My String Is Here!"
val barString = "Printing on a new line?\nNo Problem!"
val bazString = "Do you want to add a tab?\tNo Problem!"
println(fooString)
println(barString)
println(bazString)
/*
Raw 문자열은 쌍따옴표 3개(""")로 표기합니다.
Raw 문자열에는 줄바꿈이나 모든 다른 문자들을 사용할 수 있습니다.
*/
val fooRawString = """
fun helloWorld(val name : String) {
println("Hello, world!")
}
"""
println(fooRawString)
/*
문자열은 템플릿 표현식을 포함할 수 있습니다.
템플릿은 달러 기호($)로 시작합니다.
*/
val fooTemplateString = "$fooString has ${fooString.length} characters"
println(fooTemplateString)
/*
변수가 null 값을 가지려면 이를 명시적으로 선언해야 합니다.
변수 선언시 타입에 ? 표시를 붙여 nullable 을 표시합니다.
?. 연산자를 사용해 nullable 변수에 접근합니다.
?: 연산자를 이용해서 변수 값이 null 일때 사용할 값을 지정합니다.
*/
var fooNullable: String? = "abc"
println(fooNullable?.length) // => 3
println(fooNullable?.length ?: -1) // => 3
fooNullable = null
println(fooNullable?.length) // => null
println(fooNullable?.length ?: -1) // => -1
/*
함수는 "fun" 예약어를 사용해 선언합니다.
함수명 이후 괄호 안에 인자를 기술합니다.
함수 인자에 기본 값을 지정할 수도 있습니다.
함수에 리턴값이 있을 때, 필요한 경우 인자 뒤에 타입을 명시합니다.
*/
fun hello(name: String = "world"): String {
return "Hello, $name!"
}
println(hello("foo")) // => Hello, foo!
println(hello(name = "bar")) // => Hello, bar!
println(hello()) // => Hello, world!
/*
함수에 가변 인자를 넘기려면 인자에 "vararg" 예약어를 사용합니다.
*/
fun varargExample(vararg names: Int) {
println("Argument has ${names.size} elements")
}
varargExample() // => 인자가 0개 인 경우
varargExample(1) // => 인자가 1개인 경우
varargExample(1, 2, 3) // => 인자가 3개인 경우
/*
함수가 단일 표현식으로 이루어진 경우에 중괄호를 생략할 수 있습니다.
이때 함수 구현부는 = 기호 이후에 기술합니다.
*/
fun odd(x: Int): Boolean = x % 2 == 1
println(odd(6)) // => false
println(odd(7)) // => true
// 리턴 타입이 유추 가능한 경우 이를 명시하지 않아도 됩니다.
fun even(x: Int) = x % 2 == 0
println(even(6)) // => true
println(even(7)) // => false
// 함수는 함수를 인자를 받을 수 있고 함수를 리턴할 수 있습니다.
fun not(f: (Int) -> Boolean): (Int) -> Boolean {
return {n -> !f.invoke(n)}
}
// 함수는 :: 연산자를 사용해서 다른 함수에 인자로 넘길 수 있습니다.
val notOdd = not(::odd)
val notEven = not(::even)
// 람다식을 인자로 사용할 수 있습니다.
val notZero = not {n -> n == 0}
/*
하나의 인자를 가지는 람다식의 선언부와 -> 연산자는 생략될 수 있습니다.
이때 그 인자명은 it로 지정됩니다.
*/
val notPositive = not {it > 0}
for (i in 0..4) {
println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}")
}
// "class" 예약어는 클래스를 선언할 때 사용됩니다.
class ExampleClass(val x: Int) {
fun memberFunction(y: Int): Int {
return x + y
}
infix fun infixMemberFunction(y: Int): Int {
return x * y
}
}
/*
새로운 객체를 생성하기 위해서는 생성자를 바로 호출합니다.
Kotlin 에서는 new 예약어가 없다는 걸 기억하세요.
*/
val fooExampleClass = ExampleClass(7)
// 맴버 함수는 dot 표기로 호출할 수 있습니다.
println(fooExampleClass.memberFunction(4)) // => 11
/*
함수 선언에 "infix" 예약어를 사용하면 이 함수를 중위 표현식(infix notation)으로 호출할 수 있습니다
*/
println(fooExampleClass infixMemberFunction 4) // => 28
/*
데이터 클래스로 데이터만을 가지고 있는 클래스를 손쉽게 선언할 수 있습니다.
"hashCode"/"equals" 와 "toString" 는 자동으로 생성됩니다.
*/
data class DataClassExample (val x: Int, val y: Int, val z: Int)
val fooData = DataClassExample(1, 2, 4)
println(fooData) // => DataClassExample(x=1, y=2, z=4)
// 데이터 클래스는 copy 함수를 가지고 있습니다.
val fooCopy = fooData.copy(y = 100)
println(fooCopy) // => DataClassExample(x=1, y=100, z=4)
// 객체를 여러 변수로 분리할 수 있습니다.
val (a, b, c) = fooCopy
println("$a $b $c") // => 1 100 4
// "for" 루프에서 변수 분리 하기
for ((a, b, c) in listOf(fooData)) {
println("$a $b $c") // => 1 100 4
}
val mapData = mapOf("a" to 1, "b" to 2)
// Map.Entry 또한 키와 값으로 분리가 가능합니다.
for ((key, value) in mapData) {
println("$key -> $value")
}
// "with" 함수는 JavaScript 의 "with" 구문과 비슷하게 사용됩니다.
data class MutableDataClassExample (var x: Int, var y: Int, var z: Int)
val fooMutableData = MutableDataClassExample(7, 4, 9)
with (fooMutableData) {
x -= 2
y += 2
z--
}
println(fooMutableData) // => MutableDataClassExample(x=5, y=6, z=8)
/*
"listOf" 함수로 리스트를 만들 수 있습니다.
리스트는 변경 불가능(immutable)하게 만들어져 항목의 추가 삭제가 불가능합니다.
*/
val fooList = listOf("a", "b", "c")
println(fooList.size) // => 3
println(fooList.first()) // => a
println(fooList.last()) // => c
// 각 항목은 인덱스로 접근이 가능합니다.
println(fooList[1]) // => b
// 변경가능한(mutable) 리스트는 "mutableListOf" 함수로 만들 수 있습니다.
val fooMutableList = mutableListOf("a", "b", "c")
fooMutableList.add("d")
println(fooMutableList.last()) // => d
println(fooMutableList.size) // => 4
// 집합(set)은 "setOf" 함수로 만들 수 있습니다.
val fooSet = setOf("a", "b", "c")
println(fooSet.contains("a")) // => true
println(fooSet.contains("z")) // => false
// 맵은 "mapOf" 함수로 만들 수 있습니다.
val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9)
// 맵은 키를 통해 그 값에 접근할 수 있습니다. Map values can be accessed by their key.
println(fooMap["a"]) // => 8
/*
시퀀스는 지연 평가되는 컬랙션을 말합니다. Sequences represent lazily-evaluated collections.
"generateSequence" 를 사용해 시퀀스를 만들 수 있습니다. We can create a sequence using the "generateSequence" function.
*/
val fooSequence = generateSequence(1, { it + 1 })
val x = fooSequence.take(10).toList()
println(x) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// 다음은 시퀀스를 사용해서 피보나치 수열을 생성하는 예입니다.
fun fibonacciSequence(): Sequence<Long> {
var a = 0L
var b = 1L
fun next(): Long {
val result = a + b
a = b
b = result
return a
}
return generateSequence(::next)
}
val y = fibonacciSequence().take(10).toList()
println(y) // => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
// Kotlin 은 컬랙션에서 사용할 수 있는 고차(higher-order)함수를 제공합니다.
val z = (1..9).map {it * 3}
.filter {it < 20}
.groupBy {it % 2 == 0}
.mapKeys {if (it.key) "even" else "odd"}
println(z) // => {odd=[3, 9, 15], even=[6, 12, 18]}
// "for" 루프는 이터레이터를 제공하는 어떤 것과도 함께 사용할 수 있습니다.
for (c in "hello") {
println(c)
}
// "while" 루프는 다른 언어들과 동일하게 사용됩니다.
var ctr = 0
while (ctr < 5) {
println(ctr)
ctr++
}
do {
println(ctr)
ctr++
} while (ctr < 10)
/*
"if"는 값을 리턴하는 표현으로 사용될 수 있습니다.
그래서 Kotlin 에서는 삼항 ?: 연산자가 필요하지 않습니다.
*/
val num = 5
val message = if (num % 2 == 0) "even" else "odd"
println("$num is $message") // => 5 is odd
// "when"은 "if-else if" 를 대체할때 사용할 수 있습니다.
val i = 10
when {
i < 7 -> println("first block")
fooString.startsWith("hello") -> println("second block")
else -> println("else block")
}
// "when"은 인수와 함께 사용될 수 있습니다.
when (i) {
0, 21 -> println("0 or 21")
in 1..20 -> println("in the range 1 to 20")
else -> println("none of the above")
}
// "when"은 값을 리턴하는 함수처럼 사용될 수 있습니다.
var result = when (i) {
0, 21 -> "0 or 21"
in 1..20 -> "in the range 1 to 20"
else -> "none of the above"
}
println(result)
/*
객체가 어떤 타입인지를 확인하기 위해 "is" 연산자를 사용할 수 있습니다.
타입 체크를 통과하면 객체의 명시적인 형변환 없이도 그 타입 값으로 사용될 수 있습니다.
이를 스마트 변환(Smartcast)이라 부릅니다.
*/
fun smartCastExample(x: Any) : Boolean {
if (x is Boolean) {
// x is automatically cast to Boolean
return x
} else if (x is Int) {
// x is automatically cast to Int
return x > 0
} else if (x is String) {
// x is automatically cast to String
return x.isNotEmpty()
} else {
return false
}
}
println(smartCastExample("Hello, world!")) // => true
println(smartCastExample("")) // => false
println(smartCastExample(5)) // => true
println(smartCastExample(0)) // => false
println(smartCastExample(true)) // => true
// 스마트 변환은 when 블럭과도 함께 사용됩니다.
fun smartCastWhenExample(x: Any) = when (x) {
is Boolean -> x
is Int -> x > 0
is String -> x.isNotEmpty()
else -> false
}
/*
확장(Extensions)을 이용해 클래스에 새로운 기능을 추가할 수 있습니다.
C#에서의 확장 매서드와 유사합니다.
*/
fun String.remove(c: Char): String {
return this.filter {it != c}
}
println("Hello, world!".remove('l')) // => Heo, word!
println(EnumExample.A) // => A
println(ObjectExample.hello()) // => hello
}
// Enum 클래스는 자바의 enum 타입과 유사합니다.
enum class EnumExample {
A, B, C
}
/*
"object" 예약어는 싱클톤 객체를 생성할 때 사용됩니다.
객체를 새로 생성할 수는 없지만 이름을 가지고 접근해 사용할 수 있습니다.
이는 스칼라의 싱글톤 객체와 유사합니다.
*/
object ObjectExample {
fun hello(): String {
return "hello"
}
}
fun useObject() {
ObjectExample.hello()
val someRef: Any = ObjectExample // 객체의 이름을 그대로 사용합니다.
}
```
### 더 알아보기
* [Kotlin tutorials (EN)](https://kotlinlang.org/docs/tutorials/)
* [Try Kotlin in your browser (EN)](http://try.kotlinlang.org/)
* [A list of Kotlin resources (EN)](http://kotlin.link/)

421
ko/lua.md Normal file
View File

@@ -0,0 +1,421 @@
---
language: Lua
category: language
contributors:
- ["Tyler Neylon", "http://tylerneylon.com/"]
translators:
- ["wikibook", "http://wikibook.co.kr"]
lang: ko-kr
filename: learnlua-kr.lua
---
```lua
-- 대시 두 개는 한 줄짜리 주석을 의미합니다.
--[[
[와 ]를 두 개씩 추가하면 여러 줄 주석이 됩니다.
--]]
----------------------------------------------------
-- 1. 변수와 흐름 제어
----------------------------------------------------
num = 42 -- 모든 숫자는 double입니다.
-- 놀랄 필요는 없습니다. 64비트 double은
-- 정확한 int 값을 저장하기 위해 52비트로 구성돼
-- 있습니다. 52비트 이하의 int 값에 대해서는
-- 장비 정밀도와 관련된 문제가 생기지 않습니다.
s = 'walternate' -- 파이썬과 같은 불변 문자열
t = "큰따옴표를 써도 됩니다"
u = [[ 이중 대괄호는
여러 줄 문자열을
나타냅니다.]]
t = nil -- 미정의 t. 루아는 가비지 컬렉션을 지원합니다.
-- 블록은 do/end와 같은 키워드로 나타냅니다:
while num < 50 do
num = num + 1 -- ++나 += 유형의 연산자는 쓸 수 없습니다.
end
-- If 절:
if num > 40 then
print('40 이상')
elseif s ~= 'walternate' then -- ~=은 '같지 않다'입니다.
-- 동일성 검사는 파이썬과 마찬가지로 ==입니다.
-- 문자열에도 쓸 수 있습니다.
io.write('not over 40\n') -- 기본적으로 stdout에 씁니다.
else
-- 변수는 기본적으로 전역 변수입니다.
thisIsGlobal = 5 -- 낙타 표기법이 일반적입니다.
-- 변수를 지역 변수로 만드는 방법은 다음과 같습니다:
local line = io.read() -- 다음 stdin 줄을 읽습니다
-- 문자열 연결에는 .. 연산자를 씁니다:
print('겨울이 오고 있습니다, ' .. line)
end
-- 미정의 변수는 nil을 반환합니다.
-- 다음 코드를 실행해도 오류가 나지 않습니다:
foo = anUnknownVariable -- 이제 foo는 nil입니다.
aBoolValue = false
-- nil과 false만이 거짓값입니다; 0과 ''은 참입니다!
if not aBoolValue then print('twas false') end
-- 'or'와 'and'는 단축 평가(short-circuit)됩니다.
-- 다음 코드는 C/자바스크립트의 a?b:c 연산자와 비슷합니다:
ans = aBoolValue and 'yes' or 'no' --> 'no'
karlSum = 0
for i = 1, 100 do -- 범위에는 마지막 요소도 포함됩니다.
karlSum = karlSum + i
end
-- 카운트 다운을 할 때는 "100, 1, -1"을 범위로 씁니다.
fredSum = 0
for j = 100, 1, -1 do fredSum = fredSum + j end
-- 일반적으로 범위는 begin, end[, step]입니다.
-- 또 다른 반복문 구문은 다음과 같습니다:
repeat
print('미래의 방식')
num = num - 1
until num == 0
----------------------------------------------------
-- 2. 함수
----------------------------------------------------
function fib(n)
if n < 2 then return n end
return fib(n - 2) + fib(n - 1)
end
-- 클로저와 익명 함수도 사용할 수 있습니다:
function adder(x)
-- 반환된 함수는 adder가 호출될 때 생성되고 x의
-- 값이 유지됩니다:
return function (y) return x + y end
end
a1 = adder(9)
a2 = adder(36)
print(a1(16)) --> 25
print(a2(64)) --> 100
-- 반환문, 함수 호출, 할당문은 길이가 다른
-- 값의 리스트에 대해서도 모두 동작합니다.
-- 리스트에 값이 더 적을 때는 nil이 할당/반환되고
-- 리스트에 값이 더 많을 때는 나머지 값은 버려집니다.
x, y, z = 1, 2, 3, 4
-- 이제 x = 1, y = 2, z = 3이고 4는 버려집니다.
function bar(a, b, c)
print(a, b, c)
return 4, 8, 15, 16, 23, 42
end
x, y = bar('zaphod') --> "zaphod nil nil"가 출력
-- 이제 x = 4, y = 8이고 15~42의 값은 버려집니다.
-- 함수는 일급 객체이고, 지역/전역 유효범위를 가질
-- 수 있습니다. 아래의 두 함수는 같습니다:
function f(x) return x * x end
f = function (x) return x * x end
-- 그리고 아래의 두 함수도 마찬가지입니다:
local function g(x) return math.sin(x) end
local g; g = function (x) return math.sin(x) end
-- 'local g'라고 선언하면 g를 지역 함수로 만듭니다.
-- 그나저나 삼각 함수는 라디안 단위로 동작합니다.
-- 함수를 호출할 때 문자열 매개변수를 하나만 전달한다면
-- 괄호를 쓰지 않아도 됩니다:
print 'hello' -- 잘 동작합니다.
----------------------------------------------------
-- 3. 테이블
----------------------------------------------------
-- 테이블 = 루아의 유일한 복합 자료구조로서, 연관 배열입니다.
-- PHP의 배열이나 자바스크립트의 객체와 비슷하며,
-- 리스트로도 사용할 수 있는 해시 기반의 딕셔너리입니다.
-- 테이블을 딕셔너리/맵으로 사용하기:
-- 딕셔너리 리터럴은 기본적으로 문자열 키를 가집니다:
t = {key1 = 'value1', key2 = false}
-- 문자열 키에는 자바스크립트와 유사한 점 표기법을 쓸 수 있습니다:
print(t.key1) -- 'value1'을 출력.
t.newKey = {} -- 새 키/값 쌍을 추가.
t.key2 = nil -- 테이블에서 key2를 제거.
-- (nil이 아닌) 값을 키로 사용하는 리터럴 표기법:
u = {['@!#'] = 'qbert', [{}] = 1729, [6.28] = 'tau'}
print(u[6.28]) -- "tau"가 출력
-- 키 매칭은 기본적으로 숫자와 문자열에 대해서는 값으로 하지만
-- 테이블에 대해서는 식별자로 합니다.
a = u['@!#'] -- Now a = 'qbert'.
b = u[{}] -- We might expect 1729, but it's nil:
a = u['@!#'] -- 이제 a는 'qbert'입니다.
b = u[{}] -- 1729를 예상했겠지만 nil입니다:
-- 탐색이 실패하기 때문에 b는 nil입니다. 탐색이 실패하는 이유는
-- 사용된 키가 원본 값을 저장할 때 사용한 키와 동일한 객체가 아니기
-- 때문입니다. 따라서 문자열 및 숫자가 좀 더 이식성 있는 키입니다.
-- 테이블 하나를 매개변수로 취하는 함수를 호출할 때는 괄호가 필요하지 않습니다:
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'} -- 'Sonmi~451'를 출력.
for key, val in pairs(u) do -- 테이블 순회
print(key, val)
end
-- _G는 모든 전역 멤버에 대한 특별한 테이블입니다.
print(_G['_G'] == _G) -- 'true'가 출력
-- 테이블을 리스트/배열로 사용하기:
-- 리스트 리터럴은 암묵적으로 int 키로 설정됩니다:
v = {'value1', 'value2', 1.21, 'gigawatts'}
for i = 1, #v do -- #v는 리스트 v의 크기입니다.
print(v[i]) -- 인덱스가 1에서 시작합니다!! 제정신이 아닙니다!
end
-- 'list'는 실제 타입이 아닙니다. v는 연속된 정수형 키가 포함된
-- 테이블이고 리스트로 취급될 뿐입니다.
----------------------------------------------------
-- 3.1 메타테이블과 메타메서드
----------------------------------------------------
-- 테이블은 테이블에 연산자 오버로딩을 가능하게 하는 메타테이블을
-- 가질 수 있습니다. 나중에 메타테이블이 어떻게 자바스크립트
-- 프로토타입과 같은 행위를 지원하는지 살펴보겠습니다.
f1 = {a = 1, b = 2} -- 분수 a/b를 표현
f2 = {a = 2, b = 3}
-- 다음 코드는 실패합니다:
-- s = f1 + f2
metafraction = {}
function metafraction.__add(f1, f2)
sum = {}
sum.b = f1.b * f2.b
sum.a = f1.a * f2.b + f2.a * f1.b
return sum
end
setmetatable(f1, metafraction)
setmetatable(f2, metafraction)
s = f1 + f2 -- f1의 메타테이블을 대상으로 __add(f1, f2)를 호출
-- f1과 f2는 자바스크립트의 프로토타입과 달리 각 메타테이블에 대한
-- 키가 없어서 getmetatable(f1)과 같이 받아와야 합니다.
-- 메타테이블은 __add 같은 루아가 알고 있는 키가 지정된 일반 테이블입니다.
-- 그렇지만 다음 줄은 s가 메타테이블을 가지고 있지 않기 때문에 실패합니다.
-- t = s + s
-- 아래와 같이 클래스와 유사한 패턴은 이러한 문제가 발생하지 않습니다.
-- 메타테이블에 대한 __index는 점을 이용한 탐색을 오버로드합니다:
defaultFavs = {animal = 'gru', food = 'donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index = defaultFavs})
eatenBy = myFavs.animal -- 동작합니다! 고마워요, 메타테이블!
-- 직접적인 메타테이블 탐색이 실패할 경우 메타테이블의 __index 값을 이용해
-- 재시도하고, 이런 과정이 반복됩니다.
-- __index 값은 좀 더 세분화된 탐색을 위해 function(tbl, key)가
-- 될 수도 있습니다.
-- __index, __add, ...의 값을 메타메서드라고 합니다.
-- 다음은 메타메서드를 가진 테이블의 전체 목록입니다.
-- __add(a, b) for a + b
-- __sub(a, b) for a - b
-- __mul(a, b) for a * b
-- __div(a, b) for a / b
-- __mod(a, b) for a % b
-- __pow(a, b) for a ^ b
-- __unm(a) for -a
-- __concat(a, b) for a .. b
-- __len(a) for #a
-- __eq(a, b) for a == b
-- __lt(a, b) for a < b
-- __le(a, b) for a <= b
-- __index(a, b) <fn이나 테이블> for a.b
-- __newindex(a, b, c) for a.b = c
-- __call(a, ...) for a(...)
----------------------------------------------------
-- 3.2 클래스 형태의 테이블과 상속
----------------------------------------------------
-- 루아에는 클래스가 내장돼 있지 않으며, 테이블과 메타테이블을
-- 이용해 클래스를 만드는 다양한 방법이 있습니다.
-- 다음 예제에 대한 설명은 하단을 참조합니다.
Dog = {} -- 1.
function Dog:new() -- 2.
newObj = {sound = 'woof'} -- 3.
self.__index = self -- 4.
return setmetatable(newObj, self) -- 5.
end
function Dog:makeSound() -- 6.
print('I say ' .. self.sound)
end
mrDog = Dog:new() -- 7.
mrDog:makeSound() -- 'I say woof' -- 8.
-- 1. Dog는 클래스처럼 동작합니다. 실제로는 테이블입니다.
-- 2. function 테이블명:fn(...)은
-- function 테이블명.fn(self, ...)과 같습니다.
-- :는 self라는 첫 번째 인자를 추가할 뿐입니다.
-- self가 값을 어떻게 얻는지 궁금하다면 아래의 7과 8을 읽어보세요.
-- 3. newObj는 Dog 클래스의 인스턴스가 됩니다.
-- 4. self = 인스턴스화되는 클래스.
-- 주로 self = Dog이지만 상속을 이용하면 이것을 바꿀 수 있습니다.
-- newObj의 메타테이블과 self의 __index를 모두 self에 설정하면
-- newObj가 self의 함수를 갖게 됩니다.
-- 5. 참고: setmetatable은 첫 번째 인자를 반환합니다.
-- 6. :는 2에서 설명한 것과 같이 동작하지만 이번에는 self가
-- 클래스가 아닌 인스턴스라고 예상할 수 있습니다.
-- 7. Dog.new(Dog)과 같으므로 new()에서는 self = Dog입니다.
-- 8. mrDog.makeSound(mrDog)과 같으므로 self = mrDog입니다.
----------------------------------------------------
-- 상속 예제:
LoudDog = Dog:new() -- 1.
function LoudDog:makeSound()
s = self.sound .. ' ' -- 2.
print(s .. s .. s)
end
seymour = LoudDog:new() -- 3.
seymour:makeSound() -- 'woof woof woof' -- 4.
-- 1. LoudDog은 Dog의 메서드와 변수를 갖게 됩니다.
-- 2. self는 new()에서 'sound' 키를 가집니다. 3을 참고하세요.
-- 3. LoudDog.new(LoudDog)과 같고, LoudDog은 'new' 키가 없지만
-- 메타테이블에서 __index = Dog이기 때문에 Dog.new(LoudDog)으로
-- 변환됩니다.
-- 결과: seymour의 메타테이블은 LoudDog이고 LoudDog.__index는
-- LoudDog입니다. 따라서 seymour.key는 seymour.key,
-- LoudDog.key, Dog.key와 같을 것이며, 지정한 키에 어떤 테이블이
-- 오든 상관없을 것입니다.
-- 4. 'makeSound' 키는 LoudDog에서 발견할 수 있습니다.
-- 이것은 LoudDog.makeSound(seymour)와 같습니다.
-- 필요할 경우, 하위 클래스의 new()는 기반 클래스의 new()와 유사합니다.
function LoudDog:new()
newObj = {}
-- newObj를 구성
self.__index = self
return setmetatable(newObj, self)
end
----------------------------------------------------
-- 4. 모듈
----------------------------------------------------
--[[ 여기서 주석을 제거하면 이 스크립트의 나머지 부분은
-- 실행 가능한 상태가 됩니다.
```
```lua
-- mod.lua 파일의 내용이 다음과 같다고 가정해 봅시다.
local M = {}
local function sayMyName()
print('이소룡')
end
function M.sayHello()
print('안녕하세요')
sayMyName()
end
return M
-- 또 다른 파일에서는 mod.lua의 기능을 이용할 수 있습니다.
local mod = require('mod') -- mod.lua 파일을 실행
-- require는 모듈을 포함시키는 표준화된 방법입니다.
-- require는 다음과 같이 동작합니다: (캐싱돼 있지 않을 경우. 하단 참조)
-- mod.lua가 함수의 본문처럼 되므로 mod.lua 안의 지역 멤버는
-- 밖에서 볼 수 없습니다.
-- 다음 코드가 동작하는 것은 mod가 mod.lua의 M과 같기 때문입니다.
mod.sayHello() -- 이소룡 씨에게 인사를 건넵니다.
-- 다음 코드를 실행하면 오류가 발생합니다.
-- sayMyName는 mod.lua 안에서만 존재하기 때문입니다:
mod.sayMyName() -- 오류
-- require의 반환값은 캐싱되므로 require를 여러 번 실행해도
-- 파일은 최대 한 번만 실행됩니다.
-- mod2.lua에 "print('Hi')"가 들어 있다고 가정해 봅시다.
local a = require('mod2') -- Hi!를 출력
local b = require('mod2') -- print를 실행하지 않음. a=b
-- dofile은 require와 비슷하지만 캐싱을 하지 않습니다:
dofile('mod2') --> Hi!
dofile('mod2') --> Hi! (require와 달리 다시 한번 실행됨)
-- loadfile은 루아 파일을 읽어들이지만 실행하지는 않습니다
f = loadfile('mod2') -- f()를 호출해야 mod2.lua가 실행됩니다.
-- loadstring은 문자열에 대한 loadfile입니다.
g = loadstring('print(343)') -- 함수를 반환합니다.
g() -- 343이 출력됩니다. 그전까지는 아무것도 출력되지 않습니다.
--]]
```
## 참고자료
루아를 배우는 일이 흥미진진했던 이유는 [LÖVE 게임 엔진](http://love2d.org/)을 이용해
게임을 만들 수 있었기 때문입니다. 이것이 제가 루아를 배운 이유입니다.
저는 [BlackBulletIV의 "프로그래머를 위한 루아"](http://nova-fusion.com/2012/08/27/lua-for-programmers-part-1/)로
시작했습니다. 그다음으로 공식 ["프로그래밍 루아"](http://www.lua.org/pil/contents.html) 책을 읽었습니다.
그렇게 루아를 배웠습니다.
lua-users.org에 있는 [짧은 루아 레퍼런스](http://lua-users.org/files/wiki_insecure/users/thomasl/luarefv51.pdf)를
읽어두면 도움될지도 모르겠습니다.
여기서는 표준 라이브러리에 관해서는 다루지 않았습니다.
* [`string` 라이브러리](http://lua-users.org/wiki/StringLibraryTutorial)
* [`table` 라이브러리](http://lua-users.org/wiki/TableLibraryTutorial)
* [`math` 라이브러리](http://lua-users.org/wiki/MathLibraryTutorial)
* [`io` 라이브러리](http://lua-users.org/wiki/IoLibraryTutorial)
* [`os` 라이브러리](http://lua-users.org/wiki/OsLibraryTutorial)
그나저나 이 파일 전체는 유효한 루아 프로그램입니다. 이 파일을
learn.lua로 저장한 후 "`lua learn.lua`"를 실행해 보세요!
이 글은 tylerneylon.com에 처음으로 써본 글이며,
[GitHub의 Gist](https://gist.github.com/tylerneylon/5853042)에서도 확인할 수 있습니다.
루아로 즐거운 시간을 보내세요!

325
ko/markdown.md Normal file
View File

@@ -0,0 +1,325 @@
---
language: Markdown
contributors:
- ["Dan Turkel", "http://danturkel.com/"]
- ["Jacob Ward", "http://github.com/JacobCWard/"]
filename: markdown-kr.md
lang: ko-kr
---
마크다운은 2004년에 존 그루버가 창시했습니다. HTML으로 (그리고 이제는 다른 다양한 형식으로도) 쉽게 변환되는 읽고 쓰기 쉬운 문법입니다.
마크다운은 또한 파서마다 구현이 다양합니다. 본 문서는 어떤 기능이 보편적인지,
혹은 어떤 기능이 특정 파서에 종속되어 있는지 명확히 하고자 합니다.
## HTML 요소
HTML은 마크다운의 수퍼셋입니다. 모든 HTML 파일은 유효한 마크다운이라는 것입니다.
```md
<!--따라서 주석과 같은 HTML 요소들을 마크다운에 사용할 수 있으며, 마크다운 파서에 영향을
받지 않을 것입니다. 하지만 마크다운 파일에서 HTML 요소를 만든다면 그 요소의 안에서는
마크다운 문법을 사용할 수 없습니다.-->
```
## 제목
텍스트 앞에 붙이는 우물 정 기호(#)의 갯수에 따라 `<h1>`부터 `<h6>`까지의 HTML 요소를
손쉽게 작성할 수 있습니다.
```md
# <h1>입니다.
## <h2>입니다.
### <h3>입니다.
#### <h4>입니다.
##### <h5>입니다.
###### <h6>입니다.
```
또한 h1과 h2를 나타내는 다른 방법이 있습니다.
```md
h1입니다.
=============
h2입니다.
-------------
```
## 간단한 텍스트 꾸미기
마크다운으로 쉽게 텍스트를 기울이거나 굵게 할 수 있습니다.
```md
*기울인 텍스트입니다.*
_이 텍스트도 같습니다._
**굵은 텍스트입니다.**
__이 텍스트도 같습니다.__
***기울인 굵은 텍스트입니다.***
**_이 텍스트도 같습니다._**
*__이것도 같습니다.__*
```
깃헙 전용 마크다운에는 취소선도 있습니다.
```md
~~이 텍스트에는 취소선이 그려집니다.~~
```
## 문단
문단은 하나 이상의 빈 줄로 구분되는, 한 줄 이상의 인접한 텍스트입니다.
```md
문단입니다. 문단에 글을 쓰다니 재밌지 않나요?
이제 두 번째 문단입니다.
아직도 두 번째 문단입니다.
나는 세 번째 문단!
```
HTML `<br />` 태그를 삽입하고 싶으시다면, 두 개 이상의 띄어쓰기로 문단을 끝내고
새 문단을 시작할 수 있습니다.
```md
띄어쓰기 두 개로 끝나는 문단 (마우스로 긁어 보세요).
이 위에는 `<br />` 태그가 있습니다.
```
인용문은 > 문자로 쉽게 쓸 수 있습니다.
```md
> 인용문입니다. 수동으로 개행하고서
> 줄마다 `>`를 칠 수도 있고 줄을 길게 쓴 다음에 저절로 개행되게 내버려 둘 수도 있습니다.
> `>`로 시작하기만 한다면 차이가 없습니다.
> 한 단계 이상의 들여쓰기를
>> 사용할 수도 있습니다.
> 깔끔하죠?
```
## 목록
순서가 없는 목록은 별표, 더하기, 하이픈을 이용해 만들 수 있습니다.
```md
* 이거
* 저거
* 그거
```
또는
```md
+ 이거
+ 저거
+ 그거
```
또는
```md
- 이거
- 저거
- 그거
```
순서가 있는 목록은 숫자와 마침표입니다.
```md
1. 하나
2.
3.
```
숫자를 정확히 붙이지 않더라도 제대로 된 순서로 보여주겠지만, 좋은 생각은 아닙니다.
```md
1. 하나
1.
1.
```
(위의 예시와 똑같이 나타납니다.)
목록 안에 목록이 올 수도 있습니다.
```md
1. 하나
2.
3.
* 이거
* 저거
4.
```
심지어 할 일 목록도 있습니다. HTML 체크박스가 만들어집니다.
```md
x가 없는 박스들은 체크되지 않은 HTML 체크박스입니다.
- [ ] 첫 번째 할 일
- [ ] 두 번째 할 일
이 체크박스는 체크된 HTML 체크박스입니다.
- [x] 완료된 일
```
## 코드
띄어쓰기 네 개 혹은 탭 한 개로 줄을 들여씀으로서 (`<code> 요소를 사용하여`) 코드를
나타낼 수 있습니다.
```md
puts "Hello, world!"
```
탭을 더 치거나 띄어쓰기를 네 번 더 함으로써 코드를 들여쓸 수 있습니다.
```md
my_array.each do |item|
puts item
end
```
인라인 코드는 백틱 문자를 이용하여 나타냅니다. `
```md
철수는 `go_to()` 함수가 뭘 했는지도 몰랐어!
```
깃헙 전용 마크다운에서는 코드를 나타내기 위해 특별한 문법을 쓸 수 있습니다.
````md
```ruby
def foobar
puts "Hello world!"
end
```
````
위의 경우에 들여쓰기가 필요없을 뿐 아니라 <code>```</code> 뒤에 특정해 준 언어의 문법에 따라
색을 입혀줄 것입니다.
## 수평선
수평선(`<hr/>`)은 셋 이상의 별표나 하이픈을 이용해 쉽게 나타낼 수 있습니다.
띄어쓰기가 포함될 수 있습니다.
```md
***
---
- - -
****************
```
## 링크
마크다운의 장점 중 하나는 링크를 만들기 쉽다는 것입니다. 대괄호 안에 나타낼 텍스트를 쓰고
괄호 안에 URL을 쓰면 됩니다.
```md
[클릭](http://test.com/)
```
괄호 안에 따옴표를 이용해 링크에 제목을 달 수도 있습니다.
```md
[클릭](http://test.com/ "test.com으로 가기")
```
상대 경로도 유효합니다.
```md
[music으로 가기](/music/).
```
참조하는 식으로 링크를 걸 수도 있습니다.
```md
[이 ][링크]에서 더 알아보세요!
[원하신다면 ][foobar]도 참고하세요.
[링크]: http://test.com/ "좋아!"
[foobar]: http://foobar.biz/ "됐다!"
```
제목은 작은 따옴표나 괄호에 들어갈 수도 있고, 완전히 생략할 수도 있습니다. 참조는 문서의
어느 곳에든 올 수 있고 참조 ID는 유일하다면 무엇이든 될 수 있습니다.
링크 텍스트를 ID로 사용하는 "묵시적 이름"도 있습니다.
```md
[이것][]은 링크입니다.
[이것]: http://thisisalink.com/
```
하지만 보통 그렇게 추천하지는 않습니다.
## 이미지
이미지는 링크와 같지만 앞에 느낌표가 붙습니다.
```md
![이미지의 alt 속성](http://imgur.com/myimage.jpg "제목")
```
참조 방식도 가능합니다.
```md
![alt 속성][이미지]
[이미지]: relative/urls/cool/image.jpg "제목이 필요하다면 여기에"
```
## 기타
### 자동 링크
```md
<http://testwebsite.com/>와
[http://testwebsite.com/](http://testwebsite.com/)는 동일합니다.
```
### 이메일 자동 링크
```md
<foo@bar.com>
```
### 탈출 문자
```md
*별표 사이에 이 텍스트*를 치고 싶지만 기울이고 싶지는 않다면
이렇게 하시면 됩니다. \*별표 사이에 이 텍스트\*.
```
### 키보드 키
깃헙 전용 마크다운에서는 `<kbd>` 태그를 이용해 키보드 키를 나타낼 수 있습니다.
```md
컴퓨터가 멈췄다면 눌러보세요.
<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Del</kbd>
```
### 표
표는 깃헙 전용 마크다운에서만 쓸 수 있고 다소 복잡하지만, 정말 쓰고 싶으시다면
```md
| 1열 | 2열 | 3열 |
| :--------| :-------: | --------: |
| 왼쪽 정렬 | 가운데 정렬 | 오른쪽 정렬 |
| 머시기 | 머시기 | 머시기 |
```
혹은
```md
1열 | 2열 | 3열
:-- | :-: | --:
으악 너무 못생겼어 | 그만 | 둬
```
---
추가 정보를 위해, 존 그루버의 공식 문법 [(영어) 문서](http://daringfireball.net/projects/markdown/syntax)와 애덤 프릿차드의 훌륭한 [(영어) 치트싯](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet)을 확인하세요.

661
ko/php.md Normal file
View File

@@ -0,0 +1,661 @@
---
language: PHP
category: language
contributors:
- ["Malcolm Fell", "http://emarref.net/"]
- ["Trismegiste", "https://github.com/Trismegiste"]
filename: learnphp-kr.php
translators:
- ["wikibook", "http://wikibook.co.kr"]
lang: ko-kr
---
이 문서에서는 PHP 5+를 설명합니다.
```php
<?php // PHP 코드는 반드시 <?php 태그로 감싸야 합니다.
// php 파일에 PHP 코드만 들어 있다면 닫는 태그를 생략하는 것이 관례입니다.
// 슬래시 두 개는 한 줄 주석을 의미합니다.
# 해시(파운드 기호로도 알려진)도 같은 역할을 하지만 //이 더 일반적으로 쓰입니다.
/*
텍스트를 슬래시-별표와 별표-슬래시로 감싸면
여러 줄 주석이 만들어집니다.
*/
// 출력결과를 표시하려면 "echo"나 "print"를 사용합니다.
print('Hello '); // 줄바꿈 없이 "Hello "를 출력합니다.
// ()는 print와 echo를 사용할 때 선택적으로 사용할 수 있습니다.
echo "World\n"; // "World"를 출력한 후 줄바꿈합니다.
// (모든 구문은 반드시 세미콜론으로 끝나야 합니다.)
// <?php 태그 밖의 내용은 모두 자동으로 출력됩니다.
?>
Hello World Again!
<?php
/************************************
* 타입과 변수
*/
// 변수명은 $ 기호로 시작합니다.
// 유효한 변수명은 문자나 밑줄(_)로 시작하고,
// 이어서 임의 개수의 숫자나 문자, 밑줄이 옵니다.
// 불린값은 대소문자를 구분합니다.
$boolean = true; // 또는 TRUE나 True
$boolean = false; // 또는 FALSE나 False
// Integer
$int1 = 12; // => 12
$int2 = -12; // => -12
$int3 = 012; // => 10 (a leading 0 denotes an octal number)
$int4 = 0x0F; // => 15 (a leading 0x denotes a hex literal)
// Float (doubles로도 알려짐)
$float = 1.234;
$float = 1.2e3;
$float = 7E-10;
// 산술 연산
$sum = 1 + 1; // 2
$difference = 2 - 1; // 1
$product = 2 * 2; // 4
$quotient = 2 / 1; // 2
// 축약형 산술 연산
$number = 0;
$number += 1; // $number를 1만큼 증가
echo $number++; // 1을 출력(평가 후 증가)
echo ++$number; // 3 (평가 전 증가)
$number /= $float; // 나눗셈 후 몫을 $number에 할당
// 문자열은 작은따옴표로 감싸야 합니다.
$sgl_quotes = '$String'; // => '$String'
// 다른 변수를 포함할 때를 제외하면 큰따옴표 사용을 자제합니다.
$dbl_quotes = "This is a $sgl_quotes."; // => 'This is a $String.'
// 특수 문자는 큰따옴표에서만 이스케이프됩니다.
$escaped = "This contains a \t tab character.";
$unescaped = 'This just contains a slash and a t: \t';
// 필요할 경우 변수를 중괄호로 감쌉니다.
$money = "I have $${number} in the bank.";
// PHP 5.3부터는 여러 줄 문자열을 생성하는 데 나우닥(nowdoc)을 사용할 수 있습니다.
$nowdoc = <<<'END'
Multi line
string
END;
// 히어닥(heredoc)에서는 문자열 치환을 지원합니다.
$heredoc = <<<END
Multi line
$sgl_quotes
END;
// 문자열을 연결할 때는 .을 이용합니다.
echo 'This string ' . 'is concatenated';
/********************************
* 상수
*/
// 상수는 define()을 이용해 정의되며,
// 런타임 동안 절대 변경될 수 없습니다!
// 유효한 상수명은 문자나 밑줄로 시작하고,
// 이어서 임의 개수의 숫자나 문자, 밑줄이 옵니다.
define("FOO", "something");
// 상수명을 이용해 직접 상수에 접근할 수 있습니다.
echo 'This outputs '.FOO;
/********************************
* 배열
*/
// PHP의 모든 배열은 연관 배열(associative array, 해시맵)입니다.
// 일부 언어에서 해시맵으로도 알려진 연관 배열은
// 모든 PHP 버전에서 동작합니다.
$associative = array('One' => 1, 'Two' => 2, 'Three' => 3);
// PHP 5.4에서는 새로운 문법이 도입됐습니다.
$associative = ['One' => 1, 'Two' => 2, 'Three' => 3];
echo $associative['One']; // 1을 출력
// 리스트 리터럴은 암시적으로 정수형 키를 할당합니다.
$array = ['One', 'Two', 'Three'];
echo $array[0]; // => "One"
/********************************
* 출력
*/
echo('Hello World!');
// 표준출력(stdout)에 Hello World!를 출력합니다.
// 브라우저에서 실행할 경우 표준출력은 웹 페이지입니다.
print('Hello World!'); // echo과 동일
// echo는 실제로 언어 구성물에 해당하므로, 괄호를 생략할 수 있습니다.
echo 'Hello World!';
print 'Hello World!'; // 똑같이 출력됩니다.
$paragraph = 'paragraph';
echo 100; // 스칼라 변수는 곧바로 출력합니다.
echo $paragraph; // 또는 변수의 값을 출력합니다.
// 축약형 여는 태그를 설정하거나 PHP 버전이 5.4.0 이상이면
// 축약된 echo 문법을 사용할 수 있습니다.
?>
<p><?= $paragraph ?></p>
<?php
$x = 1;
$y = 2;
$x = $y; // 이제 $x의 값은 $y의 값과 같습니다.
$z = &$y;
// $z는 이제 $y에 대한 참조를 담고 있습니다. $z의 값을 변경하면
// $y의 값도 함께 변경되며, 그 반대도 마찬가지입니다.
// $x는 $y의 원래 값을 그대로 유지합니다.
echo $x; // => 2
echo $z; // => 2
$y = 0;
echo $x; // => 2
echo $z; // => 0
/********************************
* 로직
*/
$a = 0;
$b = '0';
$c = '1';
$d = '1';
// assert는 인자가 참이 아닌 경우 경고를 출력합니다.
// 다음과 같은 비교는 항상 참이며, 타입이 같지 않더라도 마찬가지입니다.
assert($a == $b); // 동일성 검사
assert($c != $a); // 불일치성 검사
assert($c <> $a); // 또 다른 불일치성 검사
assert($a < $c);
assert($c > $b);
assert($a <= $b);
assert($c >= $d);
// 다음과 같은 코드는 값과 타입이 모두 일치하는 경우에만 참입니다.
assert($c === $d);
assert($a !== $d);
assert(1 == '1');
assert(1 !== '1');
// 변수는 어떻게 사용하느냐 따라 다른 타입으로 변환될 수 있습니다.
$integer = 1;
echo $integer + $integer; // => 2
$string = '1';
echo $string + $string; // => 2 (문자열이 강제로 정수로 변환됩니다)
$string = 'one';
echo $string + $string; // => 0
// + 연산자는 'one'이라는 문자열을 숫자로 형변환할 수 없기 때문에 0이 출력됩니다.
// 한 변수를 다른 타입으로 처리하는 데 형변환을 사용할 수 있습니다.
$boolean = (boolean) 1; // => true
$zero = 0;
$boolean = (boolean) $zero; // => false
// 대다수의 타입을 형변환하는 데 사용하는 전용 함수도 있습니다.
$integer = 5;
$string = strval($integer);
$var = null; // 널 타입
/********************************
* 제어 구조
*/
if (true) {
print 'I get printed';
}
if (false) {
print 'I don\'t';
} else {
print 'I get printed';
}
if (false) {
print 'Does not get printed';
} elseif(true) {
print 'Does';
}
// 사항 연산자
print (false ? 'Does not get printed' : 'Does');
$x = 0;
if ($x === '0') {
print 'Does not print';
} elseif($x == '1') {
print 'Does not print';
} else {
print 'Does print';
}
// 다음과 같은 문법은 템플릿에 유용합니다.
?>
<?php if ($x): ?>
This is displayed if the test is truthy.
<?php else: ?>
This is displayed otherwise.
<?php endif; ?>
<?php
// 특정 로직을 표현할 때는 switch를 사용합니다.
switch ($x) {
case '0':
print 'Switch does type coercion';
break; // break을 반드시 포함해야 하며, break를 생략하면
// 'two'와 'three' 케이스로 넘어갑니다.
case 'two':
case 'three':
// 변수가 'two'나 'three'인 경우에 실행될 코드를 작성합니다.
break;
default:
// 기본값으로 실행될 코드를 작성
}
// while과 do...while, for 문이 아마 더 친숙할 것입니다.
$i = 0;
while ($i < 5) {
echo $i++;
}; // "01234"를 출력
echo "\n";
$i = 0;
do {
echo $i++;
} while ($i < 5); // "01234"를 출력
echo "\n";
for ($x = 0; $x < 10; $x++) {
echo $x;
} // "0123456789"를 출력
echo "\n";
$wheels = ['bicycle' => 2, 'car' => 4];
// foreach 문은 배영를 순회할 수 있습니다.
foreach ($wheels as $wheel_count) {
echo $wheel_count;
} // "24"를 출력
echo "\n";
// 키와 값을 동시에 순회할 수 있습니다.
foreach ($wheels as $vehicle => $wheel_count) {
echo "A $vehicle has $wheel_count wheels";
}
echo "\n";
$i = 0;
while ($i < 5) {
if ($i === 3) {
break; // while 문을 빠져나옴
}
echo $i++;
} // "012"를 출력
for ($i = 0; $i < 5; $i++) {
if ($i === 3) {
continue; // 이번 순회를 생략
}
echo $i;
} // "0124"를 출력
/********************************
* 함수
*/
// "function"으로 함수를 정의합니다.
function my_function () {
return 'Hello';
}
echo my_function(); // => "Hello"
// 유효한 함수명은 문자나 밑줄로 시작하고, 이어서
// 임의 개수의 문자나 숫자, 밑줄이 옵니다.
function add ($x, $y = 1) { // $y는 선택사항이고 기본값은 1입니다.
$result = $x + $y;
return $result;
}
echo add(4); // => 5
echo add(4, 2); // => 6
// 함수 밖에서는 $result에 접근할 수 없습니다.
// print $result; // 이 코드를 실행하면 경고가 출력됩니다.
// PHP 5.3부터는 익명 함수를 선언할 수 있습니다.
$inc = function ($x) {
return $x + 1;
};
echo $inc(2); // => 3
function foo ($x, $y, $z) {
echo "$x - $y - $z";
}
// 함수에서는 함수를 반환할 수 있습니다.
function bar ($x, $y) {
// 'use'를 이용해 바깥 함수의 변수를 전달합니다.
return function ($z) use ($x, $y) {
foo($x, $y, $z);
};
}
$bar = bar('A', 'B');
$bar('C'); // "A - B - C"를 출력
// 문자열을 이용해 이름이 지정된 함수를 호출할 수 있습니다.
$function_name = 'add';
echo $function_name(1, 2); // => 3
// 프로그램 방식으로 어느 함수를 실행할지 결정할 때 유용합니다.
// 아니면 call_user_func(callable $callback [, $parameter [, ... ]]);를 사용해도 됩니다.
/********************************
* 인클루드
*/
<?php
// 인클루드된 파일 내의 PHP 코드도 반드시 PHP 여는 태그로 시작해야 합니다.
include 'my-file.php';
// my-file.php 안의 코드는 이제 현재 유효범위에서 이용할 수 있습니다.
// 파일을 인클루드할 수 없으면(예: 파일을 찾을 수 없음) 경고가 출력됩니다.
include_once 'my-file.php';
// my-file.php 안의 코드가 다른 곳에 인클루드됐다면 다시 인클루드되지는 않습니다.
// 따라서 클래스 선언이 여러 번 되어 발생하는 문제가 일어나지 않습니다.
require 'my-file.php';
require_once 'my-file.php';
// require()는 include()와 같지만 파일을 인클루드할 수 없을 경우
// 치명적인 오류가 발생한다는 점이 다릅니다.
// my-include.php의 내용
<?php
return 'Anything you like.';
// 파일의 끝
// include와 require는 값을 반환할 수도 있습니다.
$value = include 'my-include.php';
// 파일은 지정된 파일 경로를 토대로 인클루드되거나, 혹은 아무것도 명시하지 않은 경우
// include_path라는 설정 지시지를 따릅니다. include_path에서 파일을 발견할 수 없으면
// include는 마지막으로 실패하기 전에 호출 스크립트 자체의 디렉터리와 현재 작업 디렉터리를 확인합니다.
/* */
/********************************
* 클래스
*/
// 클래스는 class라는 키워드로 정의합니다.
class MyClass
{
const MY_CONST = 'value'; // 상수
static $staticVar = 'static';
// 프로퍼티에는 반드시 가시성을 선언해야 합니다.
public $property = 'public';
public $instanceProp;
protected $prot = 'protected'; // 이 클래스와 하위 클래스에서 접근할 수 있음
private $priv = 'private'; // 이 클래스 내에서만 접근 가능
// __construct로 생성자를 만듭니다.
public function __construct($instanceProp) {
// $this로 인스턴스 변수에 접근합니다.
$this->instanceProp = $instanceProp;
}
// 메서드는 클래스 안의 함수로서 선언됩니다.
public function myMethod()
{
print 'MyClass';
}
final function youCannotOverrideMe()
{
}
public static function myStaticMethod()
{
print 'I am static';
}
}
echo MyClass::MY_CONST; // 'value' 출력
echo MyClass::$staticVar; // 'static' 출력
MyClass::myStaticMethod(); // 'I am static' 출력
// new를 사용해 클래스를 인스턴스화합니다.
$my_class = new MyClass('An instance property');
// 인자를 전달하지 않을 경우 괄호를 생략할 수 있습니다.
// ->를 이용해 클래스 멤버에 접근합니다
echo $my_class->property; // => "public"
echo $my_class->instanceProp; // => "An instance property"
$my_class->myMethod(); // => "MyClass"
// "extends"를 이용해 클래스를 확장합니다.
class MyOtherClass extends MyClass
{
function printProtectedProperty()
{
echo $this->prot;
}
// 메서드 재정의
function myMethod()
{
parent::myMethod();
print ' > MyOtherClass';
}
}
$my_other_class = new MyOtherClass('Instance prop');
$my_other_class->printProtectedProperty(); // => "protected" 출력
$my_other_class->myMethod(); // "MyClass > MyOtherClass" 출력
final class YouCannotExtendMe
{
}
// "마법 메서드(magic method)"로 설정자 메서드와 접근자 메서드를 만들 수 있습니다.
class MyMapClass
{
private $property;
public function __get($key)
{
return $this->$key;
}
public function __set($key, $value)
{
$this->$key = $value;
}
}
$x = new MyMapClass();
echo $x->property; // __get() 메서드를 사용
$x->property = 'Something'; // __set() 메서드를 사용
// 클래스는 추상화하거나(abstract 키워드를 사용해)
// 인터페이스를 구현할 수 있습니다(implments 키워드를 사용해).
// 인터페이스는 interface 키워드로 선언합니다.
interface InterfaceOne
{
public function doSomething();
}
interface InterfaceTwo
{
public function doSomethingElse();
}
// 인터페이스는 확장할 수 있습니다.
interface InterfaceThree extends InterfaceTwo
{
public function doAnotherContract();
}
abstract class MyAbstractClass implements InterfaceOne
{
public $x = 'doSomething';
}
class MyConcreteClass extends MyAbstractClass implements InterfaceTwo
{
public function doSomething()
{
echo $x;
}
public function doSomethingElse()
{
echo 'doSomethingElse';
}
}
// 클래스에서는 하나 이상의 인터페이스를 구현할 수 있습니다.
class SomeOtherClass implements InterfaceOne, InterfaceTwo
{
public function doSomething()
{
echo 'doSomething';
}
public function doSomethingElse()
{
echo 'doSomethingElse';
}
}
/********************************
* 특성
*/
// 특성(trait)은 PHP 5.4.0부터 사용 가능하며, "trait"으로 선언합니다.
trait MyTrait
{
public function myTraitMethod()
{
print 'I have MyTrait';
}
}
class MyTraitfulClass
{
use MyTrait;
}
$cls = new MyTraitfulClass();
$cls->myTraitMethod(); // "I have MyTrait"을 출력
/********************************
* 네임스페이스
*/
// 이 부분은 별도의 영역인데, 파일에서 처음으로 나타나는 문장은
// 네임스페이스 선언이어야 하기 때문입니다. 여기서는 그런 경우가 아니라고 가정합니다.
<?php
// 기본적으로 클래스는 전역 네임스페이스에 존재하며,
// 백슬래시를 이용해 명시적으로 호출할 수 있습니다.
$cls = new \MyClass();
// 파일에 대한 네임스페이스를 설정합니다.
namespace My\Namespace;
class MyClass
{
}
// (다른 파일에 들어 있는 코드)
$cls = new My\Namespace\MyClass;
// 또는 다른 네임스페이스 내에서 접근하는 경우
namespace My\Other\Namespace;
use My\Namespace\MyClass;
$cls = new MyClass();
// 혹은 네임스페이스에 별칭을 붙일 수도 있습니다.
namespace My\Other\Namespace;
use My\Namespace as SomeOtherNamespace;
$cls = new SomeOtherNamespace\MyClass();
*/
```
## 더 자세한 정보
레퍼런스와 커뮤니티 관련 내용은 [공식 PHP 문서](http://www.php.net/manual/)를 참고하세요.
최신 모범 사례에 관심이 있다면 [PHP The Right Way](http://www.phptherightway.com/)를 참고하세요.
PHP를 익히기 전에 다른 훌륭한 패키지 관리자를 지원하는 언어를 사용해본 적이 있다면 [컴포저(Composer)](http://getcomposer.org/)를 확인해 보세요.
공통 표준이 궁금하다면 PHP 프레임워크 상호운용성 그룹의 [PSR 표준](https://github.com/php-fig/fig-standards)을 참고하세요.

479
ko/pythonlegacy.md Normal file
View File

@@ -0,0 +1,479 @@
---
language: Python 2 (legacy)
category: language
contributors:
- ["Louie Dinh", "http://ldinh.ca"]
filename: learnpythonlegacy-ko.py
translators:
- ["wikibook", "http://wikibook.co.kr"]
lang: ko-kr
---
파이썬은 귀도 반 로섬이 90년대에 만들었습니다. 파이썬은 현존하는 널리 사용되는 언어 중 하나입니다.
저는 문법적 명료함에 반해 파이썬을 사랑하게 됐습니다. 파이썬은 기본적으로 실행 가능한 의사코드입니다.
참고: 이 글은 구체적으로 파이썬 2.7에 해당하는 내용을 담고 있습니다만
파이썬 2.x에도 적용할 수 있을 것입니다. 파이썬 3을 다룬 튜토리얼도 곧 나올 테니 기대하세요!
```python
# 한 줄짜리 주석은 해시로 시작합니다.
""" 여러 줄 문자열은 "를 세 개 써서 시작할 수 있고,
주석으로 자주 사용됩니다.
"""
####################################################
## 1. 기본 자료형과 연산자
####################################################
# 숫자
3 #=> 3
# 수학 연산은 예상하신 대로입니다.
1 + 1 #=> 2
8 - 1 #=> 7
10 * 2 #=> 20
35 / 5 #=> 7
# 나눗셈은 약간 까다롭습니다. 정수로 나눈 다음 결과값을 자동으로 내림합니다.
5 / 2 #=> 2
# 나눗셈 문제를 해결하려면 float에 대해 알아야 합니다.
2.0 # 이것이 float입니다.
11.0 / 4.0 #=> 2.75 훨씬 낫네요
# 괄호를 이용해 연산자 우선순위를 지정합니다.
(1 + 3) * 2 #=> 8
# 불린(Boolean) 값은 기본형입니다.
True
False
# not을 이용해 부정합니다.
not True #=> False
not False #=> True
# 동일성 연산자는 ==입니다.
1 == 1 #=> True
2 == 1 #=> False
# 불일치 연산자는 !=입니다.
1 != 1 #=> False
2 != 1 #=> True
# 그밖의 비교 연산자는 다음과 같습니다.
1 < 10 #=> True
1 > 10 #=> False
2 <= 2 #=> True
2 >= 2 #=> True
# 비교 연산을 연결할 수도 있습니다!
1 < 2 < 3 #=> True
2 < 3 < 2 #=> False
# 문자열은 "나 '로 생성합니다.
"This is a string."
'This is also a string.'
# 문자열도 연결할 수 있습니다!
"Hello " + "world!" #=> "Hello world!"
# 문자열은 문자로 구성된 리스트로 간주할 수 있습니다.
"This is a string"[0] #=> 'T'
# %는 다음과 같이 문자열을 형식화하는 데 사용할 수 있습니다:
"%s can be %s" % ("strings", "interpolated")
# 문자열을 형식화하는 새로운 방법은 format 메서드를 이용하는 것입니다.
# 이 메서드를 이용하는 방법이 더 선호됩니다.
"{0} can be {1}".format("strings", "formatted")
# 자릿수를 세기 싫다면 키워드를 이용해도 됩니다.
"{name} wants to eat {food}".format(name="Bob", food="lasagna")
# None은 객체입니다.
None #=> None
# 객체와 None을 비교할 때는 동일성 연산자인 `==`를 사용해서는 안 됩니다.
# 대신 `is`를 사용하세요.
"etc" is None #=> False
None is None #=> True
# 'is' 연산자는 객체의 식별자를 검사합니다.
# 기본형 값을 다룰 때는 이 연산자가 그다지 유용하지 않지만
# 객체를 다룰 때는 매우 유용합니다.
# None, 0, 빈 문자열/리스트는 모두 False로 평가됩니다.
# 그밖의 다른 값은 모두 True입니다
0 == False #=> True
"" == False #=> True
####################################################
## 2. 변수와 컬렉션
####################################################
# 뭔가를 출력하는 것은 상당히 쉽습니다.
print "I'm Python. Nice to meet you!"
# 변수에 값을 할당하기 전에 변수를 반드시 선언하지 않아도 됩니다.
some_var = 5 # 명명관례는 '밑줄이_포함된_소문자'입니다.
some_var #=> 5
# 미할당된 변수에 접근하면 예외가 발생합니다.
# 예외 처리에 관해서는 '제어 흐름'을 참고하세요.
some_other_var # 이름 오류가 발생
# 표현식으로도 사용할 수 있습니다.
"yahoo!" if 3 > 2 else 2 #=> "yahoo!"
# 리스트는 순차 항목을 저장합니다.
li = []
# 미리 채워진 리스트로 시작할 수도 있습니다.
other_li = [4, 5, 6]
# append를 이용해 리스트 끝에 항목을 추가합니다.
li.append(1) #li는 이제 [1]입니다.
li.append(2) #li는 이제 [1, 2]입니다.
li.append(4) #li는 이제 [1, 2, 4]입니다.
li.append(3) #li는 이제 [1, 2, 4, 3]입니다.
# pop을 이용해 끝에서부터 항목을 제거합니다.
li.pop() #=> 3이 반환되고 li는 이제 [1, 2, 4]입니다.
# 다시 넣어봅시다
li.append(3) # li는 이제 다시 [1, 2, 4, 3]가 됩니다.
# 배열에서 했던 것처럼 리스트에도 접근할 수 있습니다.
li[0] #=> 1
# 마지막 요소를 봅시다.
li[-1] #=> 3
# 범위를 벗어나서 접근하면 IndexError가 발생합니다.
li[4] # IndexError가 발생
# 슬라이스 문법을 통해 범위를 지정해서 값을 조회할 수 있습니다.
# (이 문법을 통해 간편하게 범위를 지정할 수 있습니다.)
li[1:3] #=> [2, 4]
# 앞부분을 생략합니다.
li[2:] #=> [4, 3]
# 끝부분을 생략합니다.
li[:3] #=> [1, 2, 4]
# del로 임의의 요소를 제거할 수 있습니다.
del li[2] # li is now [1, 2, 3]
# 리스트를 추가할 수도 있습니다.
li + other_li #=> [1, 2, 3, 4, 5, 6] - 참고: li와 other_li는 그대로 유지됩니다.
# extend로 리스트를 연결합니다.
li.extend(other_li) # 이제 li는 [1, 2, 3, 4, 5, 6]입니다.
# in으로 리스트 안에서 특정 요소가 존재하는지 확인합니다.
1 in li #=> True
# len으로 길이를 검사합니다.
len(li) #=> 6
# 튜플은 리스트와 비슷하지만 불변성을 띱니다.
tup = (1, 2, 3)
tup[0] #=> 1
tup[0] = 3 # TypeError가 발생
# 튜플에 대해서도 리스트에서 할 수 있는 일들을 모두 할 수 있습니다.
len(tup) #=> 3
tup + (4, 5, 6) #=> (1, 2, 3, 4, 5, 6)
tup[:2] #=> (1, 2)
2 in tup #=> True
# 튜플(또는 리스트)을 변수로 풀 수 있습니다.
a, b, c = (1, 2, 3) # 이제 a는 1, b는 2, c는 3입니다
# 괄호를 빼면 기본적으로 튜플이 만들어집니다.
d, e, f = 4, 5, 6
# 이제 두 값을 바꾸는 게 얼마나 쉬운지 확인해 보세요.
e, d = d, e # 이제 d는 5이고 e는 4입니다.
# 딕셔너리는 매핑을 저장합니다.
empty_dict = {}
# 다음은 값을 미리 채운 딕셔너리입니다.
filled_dict = {"one": 1, "two": 2, "three": 3}
# []를 이용해 값을 조회합니다.
filled_dict["one"] #=> 1
# 모든 키를 리스트로 구합니다.
filled_dict.keys() #=> ["three", "two", "one"]
# 참고 - 딕셔너리 키의 순서는 보장되지 않습니다.
# 따라서 결과가 이와 정확히 일치하지 않을 수도 있습니다.
# 모든 값을 리스트로 구합니다.
filled_dict.values() #=> [3, 2, 1]
# 참고 - 키 순서와 관련해서 위에서 설명한 내용과 같습니다.
# in으로 딕셔너리 안에 특정 키가 존재하는지 확인합니다.
"one" in filled_dict #=> True
1 in filled_dict #=> False
# 존재하지 않는 키를 조회하면 KeyError가 발생합니다.
filled_dict["four"] # KeyError
# get 메서드를 이용하면 KeyError가 발생하지 않습니다.
filled_dict.get("one") #=> 1
filled_dict.get("four") #=> None
# get 메서드는 값이 누락된 경우 기본 인자를 지원합니다.
filled_dict.get("one", 4) #=> 1
filled_dict.get("four", 4) #=> 4
# setdefault 메서드는 딕셔너리에 새 키-값 쌍을 추가하는 안전한 방법입니다.
filled_dict.setdefault("five", 5) #filled_dict["five"]는 5로 설정됩니다.
filled_dict.setdefault("five", 6) #filled_dict["five"]는 여전히 5입니다.
# 세트는 집합을 저장합니다.
empty_set = set()
# 다수의 값으로 세트를 초기화합니다.
some_set = set([1,2,2,3,4]) # 이제 some_set는 set([1, 2, 3, 4])입니다.
# 파이썬 2.7부터는 {}를 세트를 선언하는 데 사용할 수 있습니다.
filled_set = {1, 2, 2, 3, 4} # => {1 2 3 4}
# 세트에 항목을 추가합니다.
filled_set.add(5) # 이제 filled_set는 {1, 2, 3, 4, 5}입니다.
# &을 이용해 교집합을 만듭니다.
other_set = {3, 4, 5, 6}
filled_set & other_set #=> {3, 4, 5}
# |를 이용해 합집합을 만듭니다.
filled_set | other_set #=> {1, 2, 3, 4, 5, 6}
# -를 이용해 차집합을 만듭니다.
{1,2,3,4} - {2,3,5} #=> {1, 4}
# in으로 세트 안에 특정 요소가 존재하는지 검사합니다.
2 in filled_set #=> True
10 in filled_set #=> False
####################################################
## 3. 제어 흐름
####################################################
# 변수를 만들어 봅시다.
some_var = 5
# 다음은 if 문입니다. 파이썬에서는 들여쓰기가 대단히 중요합니다!
# 다음 코드를 실행하면 "some_var is smaller than 10"가 출력됩니다.
if some_var > 10:
print "some_var is totally bigger than 10."
elif some_var < 10: # elif 절은 선택사항입니다.
print "some_var is smaller than 10."
else: # 이 부분 역시 선택사항입니다.
print "some_var is indeed 10."
"""
for 루프는 리스트를 순회합니다.
아래 코드는 다음과 같은 내용을 출력합니다:
dog is a mammal
cat is a mammal
mouse is a mammal
"""
for animal in ["dog", "cat", "mouse"]:
# %로 형식화된 문자열에 값을 채워넣을 수 있습니다.
print "%s is a mammal" % animal
"""
`range(number)`는 숫자 리스트를 반환합니다.
이때 숫자 리스트의 범위는 0에서 지정한 숫자까지입니다.
아래 코드는 다음과 같은 내용을 출력합니다:
0
1
2
3
"""
for i in range(4):
print i
"""
while 루프는 조건이 더는 충족되지 않을 때까지 진행됩니다.
prints:
0
1
2
3
"""
x = 0
while x < 4:
print x
x += 1 # x = x + 1의 축약형
# try/except 블록을 이용한 예외 처리
# 파이썬 2.6 및 상위 버전에서 동작하는 코드
try:
# raise를 이용해 오류를 발생시킵니다
raise IndexError("This is an index error")
except IndexError as e:
pass # pass는 단순 no-op 연산입니다. 보통 이곳에 복구 코드를 작성합니다.
####################################################
## 4. 함수
####################################################
# 새 함수를 만들 때 def를 사용합니다.
def add(x, y):
print "x is %s and y is %s" % (x, y)
return x + y # return 문을 이용해 값을 반환합니다.
# 매개변수를 전달하면서 함수를 호출
add(5, 6) #=> "x is 5 and y is 6"가 출력되고 11이 반환됨
# 함수를 호출하는 또 다른 방법은 키워드 인자를 지정하는 방법입니다.
add(y=6, x=5) # 키워드 인자는 순서에 구애받지 않습니다.
# 위치 기반 인자를 임의 개수만큼 받는 함수를 정의할 수 있습니다.
def varargs(*args):
return args
varargs(1, 2, 3) #=> (1,2,3)
# 키워드 인자를 임의 개수만큼 받는 함수 또한 정의할 수 있습니다.
def keyword_args(**kwargs):
return kwargs
# 이 함수를 호출해서 어떤 일이 일어나는지 확인해 봅시다.
keyword_args(big="foot", loch="ness") #=> {"big": "foot", "loch": "ness"}
# 원한다면 한 번에 두 가지 종류의 인자를 모두 받는 함수를 정의할 수도 있습니다.
def all_the_args(*args, **kwargs):
print args
print kwargs
"""
all_the_args(1, 2, a=3, b=4)를 실행하면 다음과 같은 내용이 출력됩니다:
(1, 2)
{"a": 3, "b": 4}
"""
# 함수를 호출할 때 varargs/kwargs와 반대되는 일을 할 수 있습니다!
# *를 이용해 튜플을 확장하고 **를 이용해 kwargs를 확장합니다.
args = (1, 2, 3, 4)
kwargs = {"a": 3, "b": 4}
all_the_args(*args) # foo(1, 2, 3, 4)와 같음
all_the_args(**kwargs) # foo(a=3, b=4)와 같음
all_the_args(*args, **kwargs) # foo(1, 2, 3, 4, a=3, b=4)와 같음
# 파이썬에는 일급 함수가 있습니다
def create_adder(x):
def adder(y):
return x + y
return adder
add_10 = create_adder(10)
add_10(3) #=> 13
# 게다가 익명 함수도 있습니다.
(lambda x: x > 2)(3) #=> True
# 내장된 고차 함수(high order function)도 있습니다.
map(add_10, [1,2,3]) #=> [11, 12, 13]
filter(lambda x: x > 5, [3, 4, 5, 6, 7]) #=> [6, 7]
# 맵과 필터에 리스트 조건 제시법(list comprehensions)을 사용할 수 있습니다.
[add_10(i) for i in [1, 2, 3]] #=> [11, 12, 13]
[x for x in [3, 4, 5, 6, 7] if x > 5] #=> [6, 7]
####################################################
## 5. 클래스
####################################################
# 클래스를 하나 만들기 위해 특정 객체의 하위 클래스를 만들 수 있습니다.
class Human(object):
# 클래스 속성은 이 클래스의 모든 인스턴스에서 공유합니다.
species = "H. sapiens"
# 기본 초기화자
def __init__(self, name):
# 인자를 인스턴스의 name 속성에 할당합니다.
self.name = name
# 모든 인스턴스 메서드에서는 self를 첫 번째 인자로 받습니다.
def say(self, msg):
return "%s: %s" % (self.name, msg)
# 클래스 메서드는 모든 인스턴스에서 공유합니다.
# 클래스 메서드는 호출하는 클래스를 첫 번째 인자로 호출됩니다.
@classmethod
def get_species(cls):
return cls.species
# 정적 메서드는 클래스나 인스턴스 참조 없이도 호출할 수 있습니다.
@staticmethod
def grunt():
return "*grunt*"
# 클래스 인스턴스화
i = Human(name="Ian")
print i.say("hi") # "Ian: hi"가 출력됨
j = Human("Joel")
print j.say("hello") # "Joel: hello"가 출력됨
# 클래스 메서드를 호출
i.get_species() #=> "H. sapiens"
# 공유 속성을 변경
Human.species = "H. neanderthalensis"
i.get_species() #=> "H. neanderthalensis"
j.get_species() #=> "H. neanderthalensis"
# 정적 메서드를 호출
Human.grunt() #=> "*grunt*"
####################################################
## 6. 모듈
####################################################
# 다음과 같이 모듈을 임포트할 수 있습니다.
import math
print math.sqrt(16) #=> 4.0
# 모듈의 특정 함수를 호출할 수 있습니다.
from math import ceil, floor
print ceil(3.7) #=> 4.0
print floor(3.7) #=> 3.0
# 모듈의 모든 함수를 임포트할 수 있습니다.
# Warning: this is not recommended
from math import *
# 모듈 이름을 축약해서 쓸 수 있습니다.
import math as m
math.sqrt(16) == m.sqrt(16) #=> True
# 파이썬 모듈은 평범한 파이썬 파일에 불과합니다.
# 직접 모듈을 작성해서 그것들을 임포트할 수 있습니다.
# 모듈의 이름은 파일의 이름과 같습니다.
# 다음과 같은 코드로 모듈을 구성하는 함수와 속성을 확인할 수 있습니다.
import math
dir(math)
```
## 더 배울 준비가 되셨습니까?
### 무료 온라인 참고자료
* [Learn Python The Hard Way](http://learnpythonthehardway.org/book/)
* [Dive Into Python](http://www.diveintopython.net/)
* [The Official Docs](http://docs.python.org/2.6/)
* [Hitchhiker's Guide to Python](http://docs.python-guide.org/en/latest/)
* [Python Module of the Week](http://pymotw.com/2/)
### 파이썬 관련 도서
* [Programming Python](http://www.amazon.com/gp/product/0596158106/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596158106&linkCode=as2&tag=homebits04-20)
* [Dive Into Python](http://www.amazon.com/gp/product/1441413022/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1441413022&linkCode=as2&tag=homebits04-20)
* [Python Essential Reference](http://www.amazon.com/gp/product/0672329786/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0672329786&linkCode=as2&tag=homebits04-20)

640
ko/racket.md Normal file
View File

@@ -0,0 +1,640 @@
---
language: Racket
filename: learnracket-kr.rkt
contributors:
- ["th3rac25", "https://github.com/voila"]
- ["Eli Barzilay", "https://github.com/elibarzilay"]
- ["Gustavo Schmidt", "https://github.com/gustavoschmidt"]
- ["Duong H. Nguyen", "https://github.com/cmpitg"]
translators:
- ["KIM Taegyoon", "https://github.com/kimtg"]
lang: ko-kr
---
Racket 은 Lisp/Scheme 계열의 일반 목적의, 다중 패러다임 프로그래밍 언어이다.
```racket
#lang racket ; 우리가 사용하는 언어를 정의한다.
;;; 주석
;; 한 줄 주석은 세미콜론으로 시작한다.
#| 블록 주석
은 여러 줄에 걸칠 수 있으며...
#|
중첩될 수 있다!
|#
|#
;; S-expression 주석은 아래 식을 버리므로,
;; 디버깅할 때 식을 주석화할 때 유용하다.
#; ( 식은 버려짐)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 1. 근본 자료형과 연산자
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 숫자
9999999999999999999999 ; 정수
#b111 ; 이진수 => 7
#o111 ; 팔진수 => 73
#x111 ; 16진수 => 273
3.14 ; 실수
6.02e+23
1/2 ; 분수
1+2i ; 복소수
;; 함수 적용은 이렇게 쓴다: (f x y z ...)
;; 여기에서 f는 함수이고 x, y, z는 피연산자이다.
;; 글자 그대로의 데이터 리스트를 만들고 싶다면 평가를 막기 위해 '를 쓰시오.
'(+ 1 2) ; => (+ 1 2)
;; 이제, 산술 연산 몇 개
(+ 1 1) ; => 2
(- 8 1) ; => 7
(* 10 2) ; => 20
(expt 2 3) ; => 8
(quotient 5 2) ; => 2
(remainder 5 2) ; => 1
(/ 35 5) ; => 7
(/ 1 3) ; => 1/3
(exact->inexact 1/3) ; => 0.3333333333333333
(+ 1+2i 2-3i) ; => 3-1i
;;; 불린
#t ; 참
#f ; 거짓 -- #f가 아닌 것은 참
(not #t) ; => #f
(and 0 #f (error "doesn't get here")) ; => #f
(or #f 0 (error "doesn't get here")) ; => 0
;;; 문자
#\A ; => #\A
#\λ ; => #\λ
#\u03BB ; => #\λ
;;; 문자열은 고정 길이의 문자 배열이다.
"Hello, world!"
"Benjamin \"Bugsy\" Siegel" ; 백슬래시는 탈출 문자이다.
"Foo\tbar\41\x21\u0021\a\r\n" ; C 탈출 문자, 유니코드 포함
"λx:(μα.α→α).xx" ; 유니코드 문자 포함 가능
;; 문자열은 붙여질 수 있다!
(string-append "Hello " "world!") ; => "Hello world!"
;; 문자열은 문자의 리스트처럼 취급될 수 있다.
(string-ref "Apple" 0) ; => #\A
;; format은 문자열을 형식화하기 위해 사용된다:
(format "~a can be ~a" "strings" "formatted")
;; 인쇄는 쉽다.
(printf "I'm Racket. Nice to meet you!\n")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 2. 변수
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; define으로 변수를 만든다.
;; 변수명으로 다음 문자를 사용할 수 없다: ()[]{}",'`;#|\
(define some-var 5)
some-var ; => 5
;; 유니코드 문자도 사용 가능하다.
(define subset?)
( (set 3 2) (set 1 2 3)) ; => #t
;; 앞에서 정의되지 않은 변수에 접근하면 예외가 발생한다.
; x ; => x: undefined ...
;; 지역 변수: `me'는 (let ...) 안에서만 "Bob"이다.
(let ([me "Bob"])
"Alice"
me) ; => "Bob"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. 구조체(Struct)와 모음(Collection)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 구조체
(struct dog (name breed age))
(define my-pet
(dog "lassie" "collie" 5))
my-pet ; => #<dog>
(dog? my-pet) ; => #t
(dog-name my-pet) ; => "lassie"
;;; 쌍 (불변)
;; `cons'는 쌍을 만들고, `car'와 `cdr'는 첫번째와
;; 두번째 원소를 추출한다.
(cons 1 2) ; => '(1 . 2)
(car (cons 1 2)) ; => 1
(cdr (cons 1 2)) ; => 2
;;; 리스트
;; 리스트는 연결-리스트 데이터 구조이며, `cons' 쌍으로 만들어지며
;; `null' (또는 '()) 로 리스트의 끝을 표시한다.
(cons 1 (cons 2 (cons 3 null))) ; => '(1 2 3)
;; `list'는 편리한 가변인자 리스트 생성자이다.
(list 1 2 3) ; => '(1 2 3)
;; 글자 그대로의 리스트 값에는 인용부호를 쓴다.
'(1 2 3) ; => '(1 2 3)
;; 리스트의 앞에 항목을 추가하기 위하여 `cons'를 사용한다.
(cons 4 '(1 2 3)) ; => '(4 1 2 3)
;; 리스트들을 붙이기 위해 `append'를 사용한다.
(append '(1 2) '(3 4)) ; => '(1 2 3 4)
;; 리스트는 매우 기본적인 자료형이기 때문에, 리스트에 대해 적용되는 많은 기능들이 있다.
;; 예를 들어:
(map add1 '(1 2 3)) ; => '(2 3 4)
(map + '(1 2 3) '(10 20 30)) ; => '(11 22 33)
(filter even? '(1 2 3 4)) ; => '(2 4)
(count even? '(1 2 3 4)) ; => 2
(take '(1 2 3 4) 2) ; => '(1 2)
(drop '(1 2 3 4) 2) ; => '(3 4)
;;; 벡터
;; 벡터는 고정 길이의 배열이다.
#(1 2 3) ; => '#(1 2 3)
;; `vector-append'를 사용하여 벡터들을 붙인다.
(vector-append #(1 2 3) #(4 5 6)) ; => #(1 2 3 4 5 6)
;;; 집합
;; 리스트로부터 집합 만들기
(list->set '(1 2 3 1 2 3 3 2 1 3 2 1)) ; => (set 1 2 3)
;; 원소를 추가하려면 `set-add'를 사용한다.
;; (함수적: 확장된 집합을 반환하며, 원래의 입력을 변경하지 않는다.)
(set-add (set 1 2 3) 4) ; => (set 1 2 3 4)
;; 원소를 삭제하려면 `set-remove'
(set-remove (set 1 2 3) 1) ; => (set 2 3)
;; 존재 여부를 조사하려면 `set-member?'
(set-member? (set 1 2 3) 1) ; => #t
(set-member? (set 1 2 3) 4) ; => #f
;;; 해시
;; 불변의 해시 테이블을 만든다. (가변 예제는 아래에)
(define m (hash 'a 1 'b 2 'c 3))
;; 값 꺼내기
(hash-ref m 'a) ; => 1
;; 없는 값을 꺼내는 것은 예외를 발생시킨다.
; (hash-ref m 'd) => no value found
;; 키가 없을 때 반환할 기본값을 지정할 수 있다.
(hash-ref m 'd 0) ; => 0
;; `hash-set'을 사용하여 불변의 해시 테이블을 확장
;; (원래 것을 변경하지 않고 확장된 해시를 반환한다.)
(define m2 (hash-set m 'd 4))
m2 ; => '#hash((b . 2) (a . 1) (d . 4) (c . 3))
;; 이 해시들은 불변이라는 점을 기억하시오!
m ; => '#hash((b . 2) (a . 1) (c . 3)) <-- no `d'
;; `hash-remove'로 키를 삭제 (이것도 함수적)
(hash-remove m 'a) ; => '#hash((b . 2) (c . 3))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 3. 함수
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; `lambda'로 함수를 만든다.
;; 함수는 항상 마지막 식을 반환한다.
(lambda () "Hello World") ; => #<procedure>
;; 유니코드 `λ'도 사용 가능
(λ () "Hello World") ; => same function
;; 모든 함수를 호출할 때는 괄호를 쓴다, lambda 식도 포함하여.
((lambda () "Hello World")) ; => "Hello World"
((λ () "Hello World")) ; => "Hello World"
;; 변수에 함수를 할당
(define hello-world (lambda () "Hello World"))
(hello-world) ; => "Hello World"
;; 문법적 설탕을 사용하여 함수 정의를 더 짧게할 수 있다:
(define (hello-world2) "Hello World")
;; 위에서 ()는 함수의 인자 리스트이다.
(define hello
(lambda (name)
(string-append "Hello " name)))
(hello "Steve") ; => "Hello Steve"
;; ... 또는, 설탕 친 정의로:
(define (hello2 name)
(string-append "Hello " name))
;; 가변인자 함수에는 `case-lambda'를 사용한다.
(define hello3
(case-lambda
[() "Hello World"]
[(name) (string-append "Hello " name)]))
(hello3 "Jake") ; => "Hello Jake"
(hello3) ; => "Hello World"
;; ... 또는 선택적 인자에 기본값 지정
(define (hello4 [name "World"])
(string-append "Hello " name))
;; 함수는 추가 인자를 리스트에 포장할 수 있다.
(define (count-args . args)
(format "You passed ~a args: ~a" (length args) args))
(count-args 1 2 3) ; => "You passed 3 args: (1 2 3)"
;; ... 설탕 안 친 `lambda' 형식으로는:
(define count-args2
(lambda args
(format "You passed ~a args: ~a" (length args) args)))
;; 일반 인자와 포장된 인자를 섞을 수 있다.
(define (hello-count name . args)
(format "Hello ~a, you passed ~a extra args" name (length args)))
(hello-count "Finn" 1 2 3)
; => "Hello Finn, you passed 3 extra args"
;; ... 설탕 안 친 것:
(define hello-count2
(lambda (name . args)
(format "Hello ~a, you passed ~a extra args" name (length args))))
;; 키워드 인자
(define (hello-k #:name [name "World"] #:greeting [g "Hello"] . args)
(format "~a ~a, ~a extra args" g name (length args)))
(hello-k) ; => "Hello World, 0 extra args"
(hello-k 1 2 3) ; => "Hello World, 3 extra args"
(hello-k #:greeting "Hi") ; => "Hi World, 0 extra args"
(hello-k #:name "Finn" #:greeting "Hey") ; => "Hey Finn, 0 extra args"
(hello-k 1 2 3 #:greeting "Hi" #:name "Finn" 4 5 6)
; => "Hi Finn, 6 extra args"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 4. 동등성
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 숫자에는 `='를 사용하시오.
(= 3 3.0) ; => #t
(= 2 1) ; => #f
;; 개체의 동등성에는 `eq?'를 사용하시오.
(eq? 3 3) ; => #t
(eq? 3 3.0) ; => #f
(eq? (list 3) (list 3)) ; => #f
;; 모음에는 `equal?'을 사용하시오.
(equal? (list 'a 'b) (list 'a 'b)) ; => #t
(equal? (list 'a 'b) (list 'b 'a)) ; => #f
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 5. 흐름 제어하기
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; 조건
(if #t ; 조사 식
"this is true" ; 그러면 식
"this is false") ; 아니면 식
; => "this is true"
;; 조건에서는 #f가 아니면 참으로 취급된다.
(member 'Groucho '(Harpo Groucho Zeppo)) ; => '(Groucho Zeppo)
(if (member 'Groucho '(Harpo Groucho Zeppo))
'yep
'nope)
; => 'yep
;; `cond'는 연속하여 조사하여 값을 선택한다.
(cond [(> 2 2) (error "wrong!")]
[(< 2 2) (error "wrong again!")]
[else 'ok]) ; => 'ok
;;; 양식 맞춤
(define (fizzbuzz? n)
(match (list (remainder n 3) (remainder n 5))
[(list 0 0) 'fizzbuzz]
[(list 0 _) 'fizz]
[(list _ 0) 'buzz]
[_ #f]))
(fizzbuzz? 15) ; => 'fizzbuzz
(fizzbuzz? 37) ; => #f
;;; 반복
;; 반복은 (꼬리-) 재귀로 한다.
(define (loop i)
(when (< i 10)
(printf "i=~a\n" i)
(loop (add1 i))))
(loop 5) ; => i=5, i=6, ...
;; 이름 있는 let으로도...
(let loop ((i 0))
(when (< i 10)
(printf "i=~a\n" i)
(loop (add1 i)))) ; => i=0, i=1, ...
;; Racket은 매우 유연한 `for' 형식을 가지고 있다:
(for ([i 10])
(printf "i=~a\n" i)) ; => i=0, i=1, ...
(for ([i (in-range 5 10)])
(printf "i=~a\n" i)) ; => i=5, i=6, ...
;;; 다른 Sequence들을 순회하는 반복
;; `for'는 여러 가지의 sequence를 순회할 수 있다:
;; 리스트, 벡터, 문자열, 집합, 해시 테이블 등...
(for ([i (in-list '(l i s t))])
(displayln i))
(for ([i (in-vector #(v e c t o r))])
(displayln i))
(for ([i (in-string "string")])
(displayln i))
(for ([i (in-set (set 'x 'y 'z))])
(displayln i))
(for ([(k v) (in-hash (hash 'a 1 'b 2 'c 3 ))])
(printf "key:~a value:~a\n" k v))
;;; 더 복잡한 반복
;; 여러 sequence에 대한 병렬 순회 (가장 짧은 것 기준으로 중단)
(for ([i 10] [j '(x y z)]) (printf "~a:~a\n" i j))
; => 0:x 1:y 2:z
;; 중첩 반복
(for* ([i 2] [j '(x y z)]) (printf "~a:~a\n" i j))
; => 0:x, 0:y, 0:z, 1:x, 1:y, 1:z
;; 조건
(for ([i 1000]
#:when (> i 5)
#:unless (odd? i)
#:break (> i 10))
(printf "i=~a\n" i))
; => i=6, i=8, i=10
;;; 함축
;; `for' 반복과 비슷하며, 결과만 수집한다.
(for/list ([i '(1 2 3)])
(add1 i)) ; => '(2 3 4)
(for/list ([i '(1 2 3)] #:when (even? i))
i) ; => '(2)
(for/list ([i 10] [j '(x y z)])
(list i j)) ; => '((0 x) (1 y) (2 z))
(for/list ([i 1000] #:when (> i 5) #:unless (odd? i) #:break (> i 10))
i) ; => '(6 8 10)
(for/hash ([i '(1 2 3)])
(values i (number->string i)))
; => '#hash((1 . "1") (2 . "2") (3 . "3"))
;; 반복의 값을 수집하는 여러 가지 방법이 있다:
(for/sum ([i 10]) (* i i)) ; => 285
(for/product ([i (in-range 1 11)]) (* i i)) ; => 13168189440000
(for/and ([i 10] [j (in-range 10 20)]) (< i j)) ; => #t
(for/or ([i 10] [j (in-range 0 20 2)]) (= i j)) ; => #t
;; 임의의 조합을 사용하려면 `for/fold'를 사용:
(for/fold ([sum 0]) ([i '(1 2 3 4)]) (+ sum i)) ; => 10
;; (이것은 명령형 반복문을 대체하기도 한다.)
;;; 예외
;; 예외를 잡으려면 `with-handlers' 형식을 사용
(with-handlers ([exn:fail? (lambda (exn) 999)])
(+ 1 "2")) ; => 999
(with-handlers ([exn:break? (lambda (exn) "no time")])
(sleep 3)
"phew") ; => "phew", but if you break it => "no time"
;; 예외나 다른 값을 던지려면 `raise'를 사용
(with-handlers ([number? ; catch numeric values raised
identity]) ; return them as plain values
(+ 1 (raise 2))) ; => 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 6. 변경
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 기존 변수에 새 값을 할당하려면 `set!'을 사용한다.
(define n 5)
(set! n (add1 n))
n ; => 6
;; 명시적인 가변 값을 사용하려면 box 사용 (다른 언어의 포인터나 참조와 비슷함)
(define n* (box 5))
(set-box! n* (add1 (unbox n*)))
(unbox n*) ; => 6
;; 많은 Racket 자료형은 불변이다 (쌍, 리스트 등). 그러나 어떤 것들은
;; 가변과 불변형이 둘 다 있다. (string, vector, hash table 등)
;; `vector'나 `make-vector'로 가변 벡터를 생성한다.
(define vec (vector 2 2 3 4))
(define wall (make-vector 100 'bottle-of-beer))
;; 칸을 변경하려면 vector-set!을 사용한다.
(vector-set! vec 0 1)
(vector-set! wall 99 'down)
vec ; => #(1 2 3 4)
;; 비어 있는 가변 해시 테이블을 만들고 조작한다.
(define m3 (make-hash))
(hash-set! m3 'a 1)
(hash-set! m3 'b 2)
(hash-set! m3 'c 3)
(hash-ref m3 'a) ; => 1
(hash-ref m3 'd 0) ; => 0
(hash-remove! m3 'a)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 7. 모듈
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 모듈은 코드를 여러 파일과 재사용 가능한 라이브러리로 조직하게 한다.
;; 여기서 우리는 서브-모듈을 사용한다. 이 글이 만드는 전체 모듈("lang" 줄 부터 시작)에 포함된 모듈이다.
(module cake racket/base ; racket/base 기반의 `cake' 모듈 정의
(provide print-cake) ; 모듈이 노출(export)시키는 함수
(define (print-cake n)
(show " ~a " n #\.)
(show " .-~a-. " n #\|)
(show " | ~a | " n #\space)
(show "---~a---" n #\-))
(define (show fmt n ch) ; 내부 함수
(printf fmt (make-string n ch))
(newline)))
;; `require'를 사용하여 모듈에서 모든 `provide'된 이름을 사용한다.
(require 'cake) ; '는 지역 지역 서브-모듈을 위한 것이다.
(print-cake 3)
; (show "~a" 1 #\A) ; => 에러, `show'가 export되지 않았음
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 8. 클래스와 개체
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 클래스 fish%를 생성한다. (-%는 클래스 정의에 쓰이는 관용구)
(define fish%
(class object%
(init size) ; 초기화 인자
(super-new) ; 상위 클래스 초기화
;; 필드
(define current-size size)
;; 공용 메서드
(define/public (get-size)
current-size)
(define/public (grow amt)
(set! current-size (+ amt current-size)))
(define/public (eat other-fish)
(grow (send other-fish get-size)))))
;; fish%의 인스턴스를 생성한다.
(define charlie
(new fish% [size 10]))
;; 개체의 메서드를 호출하기 위해 `send'를 사용한다.
(send charlie get-size) ; => 10
(send charlie grow 6)
(send charlie get-size) ; => 16
;; `fish%'는 보통의 "일급" 값이며, mixin을 줄 수 있다.
(define (add-color c%)
(class c%
(init color)
(super-new)
(define my-color color)
(define/public (get-color) my-color)))
(define colored-fish% (add-color fish%))
(define charlie2 (new colored-fish% [size 10] [color 'red]))
(send charlie2 get-color)
;; 또는, 이름 없이:
(send (new (add-color fish%) [size 10] [color 'red]) get-color)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 9. 매크로
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 매크로는 언어의 문법을 확장할 수 있게 한다.
;; while 반복문을 추가하자.
(define-syntax-rule (while condition body ...)
(let loop ()
(when condition
body ...
(loop))))
(let ([i 0])
(while (< i 10)
(displayln i)
(set! i (add1 i))))
;; 매크로는 위생적이다. 즉, 기존 변수를 침범할 수 없다.
(define-syntax-rule (swap! x y) ; -!는 변경의 관용구
(let ([tmp x])
(set! x y)
(set! y tmp)))
(define tmp 2)
(define other 3)
(swap! tmp other)
(printf "tmp = ~a; other = ~a\n" tmp other)
;; `tmp` 변수는 이름 충돌을 피하기 위해 `tmp_1`로 이름이 변경된다.
;; (let ([tmp_1 tmp])
;; (set! tmp other)
;; (set! other tmp_1))
;; 하지만 그것들은 단지 코드 변형일 뿐이다. 예를 들어:
(define-syntax-rule (bad-while condition body ...)
(when condition
body ...
(bad-while condition body ...)))
;; 이 매크로는 엉터리다: 무한 코드를 생성하며,
;; 이것을 사용하려고 하면 컴파일러가 무한 반복에 빠진다.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 10. 계약(Contract)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 계약은 모듈에서 노출된 값에 대해 제약을 부여한다.
(module bank-account racket
(provide (contract-out
[deposit (-> positive? any)] ; 값은 양수여야 함
[balance (-> positive?)]))
(define amount 0)
(define (deposit a) (set! amount (+ amount a)))
(define (balance) amount)
)
(require 'bank-account)
(deposit 5)
(balance) ; => 5
;; 양수가 아닌 값을 예치하려고 하는 고객은 비난받는다.
;; (deposit -5) ; => deposit: contract violation
;; expected: positive?
;; given: -5
;; more details....
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 11. 입력과 출력
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Racket은 이 "port"라는 개념이 있다. 이것은 다른 언어의
;; 파일 서술자 (file descriptor)와 매우 비슷하다.
;; "/tmp/tmp.txt"를 열고 "Hello World"를 기록한다.
;; 그 파일이 이미 있다면 에러를 발생시킨다.
(define out-port (open-output-file "/tmp/tmp.txt"))
(displayln "Hello World" out-port)
(close-output-port out-port)
;; "/tmp/tmp.txt"에 붙이기
(define out-port (open-output-file "/tmp/tmp.txt"
#:exists 'append))
(displayln "Hola mundo" out-port)
(close-output-port out-port)
;; 파일에서 다시 읽기
(define in-port (open-input-file "/tmp/tmp.txt"))
(displayln (read-line in-port))
; => "Hello World"
(displayln (read-line in-port))
; => "Hola mundo"
(close-input-port in-port)
;; 다르게, call-with-output-file을 사용하면, 명시적으로 파일을 닫지 않아도 된다.
(call-with-output-file "/tmp/tmp.txt"
#:exists 'update ; 내용을 다시 쓴다.
(λ (out-port)
(displayln "World Hello!" out-port)))
;; call-with-input-file은 입력에 대해 같은 방식으로 작동한다.
(call-with-input-file "/tmp/tmp.txt"
(λ (in-port)
(displayln (read-line in-port))))
```
## 더 읽을거리
더 배우고 싶으면, [Getting Started with Racket](http://docs.racket-lang.org/getting-started/)도 보시오.

269
ko/vim.md Normal file
View File

@@ -0,0 +1,269 @@
---
category: tool
tool: Vim
contributors:
- ["RadhikaG", "https://github.com/RadhikaG"]
translators:
- ["Wooseop Kim", "https://github.com/linterpreteur"]
- ["Yeongjae Jang", "https://github.com/Liberatedwinner"]
filename: LearnVim-kr.txt
lang: ko-kr
---
[Vim](http://www.vim.org)
(Vi IMproved)은 유닉스에서 인기 있는 vi 에디터의 클론입니다. Vim은 속도와 생산성을 위해
설계된 텍스트 에디터로, 대부분의 유닉스 기반 시스템에 내장되어 있습니다. 다양한 단축 키를 통해
파일 안에서 빠르게 이동하고 편집할 수 있습니다.
## Vim 조작의 기본
```
vim <filename> # vim으로 <filename> 열기
:help <topic> # (존재하는 경우에) <topic>에 대한, 내장된 도움말 문서 열기
:q # vim 종료
:w # 현재 파일 저장
:wq # 파일 저장 후 종료
ZZ # 파일 저장 후 종료
:q! # 저장하지 않고 종료
# ! *강제로* :q를 실행하여, 저장 없이 종료
:x # 파일 저장 후 종료 (:wq의 축약)
u # 동작 취소
CTRL+R # 되돌리기
h # 한 글자 왼쪽으로 이동
j # 한 줄 아래로 이동
k # 한 줄 위로 이동
l # 한 글자 오른쪽으로 이동
# 줄 안에서의 이동
0 # 줄 시작으로 이동
$ # 줄 끝으로 이동
^ # 줄의 공백이 아닌 첫 문자로 이동
Ctrl+B # 한 화면 뒤로 이동
Ctrl+F # 한 화면 앞으로 이동
Ctrl+D # 반 화면 앞으로 이동
Ctrl+U # 반 화면 뒤로 이동
# 텍스트 검색
/word # 커서 뒤에 나타나는 해당 단어를 모두 하이라이트
?word # 커서 앞에 나타나는 해당 단어를 모두 하이라이트
n # 해당 단어를 검색 후 다음으로 나타나는 위치로 이동
N # 이전에 나타나는 위치로 이동
:%s/foo/bar/g # 파일 모든 줄에 있는 'foo'를 'bar'로 치환
:s/foo/bar/g # 현재 줄에 있는 'foo'를 'bar'로 치환
:%s/foo/bar/gc # 사용자에게 확인을 요구하는, 모든 줄에 있는 'foo'를 'bar'로 치환
:%s/\n/\r/g # 한 종류의 개행 문자에서 다른 종류의 것으로 치환 (\n에서 \r로)
# 문자로 이동
f<character> # <character>로 건너뛰기
t<character> # <character>의 바로 뒤로 건너뛰기
# 예를 들어,
f< # <로 건너뛰기
t< # <의 바로 뒤로 건너뛰기
# 단어 단위로 이동
w # 한 단어 오른쪽으로 이동
b # 한 단어 왼쪽으로 이동
e # 현재 단어의 끝으로 이동
# 기타 이동 명령어
gg # 파일 맨 위로 이동
G # 파일 맨 아래로 이동
:NUM # 줄 수 NUM(숫자)로 가기
H # 화면 꼭대기로 이동
M # 화면 중간으로 이동
L # 화면 바닥으로 이동
```
## 도움말 문서
Vim은 `:help <topic>` 명령을 통해 접근할 수 있는 도움말 문서를 내장하고 있습니다.
예를 들어, `:help navigation` 은 당신의 작업 공간을 탐색하는 방법에 대한 문서를 표시합니다!
`:help`는 옵션 없이도 사용할 수 있습니다. 이는 기본 도움말 대화 상자를 표시합니다.
이 대화 상자는 Vim을 시작하는 것이 보다 용이하도록 도와줍니다.
## 모드
Vim은 **모드**의 개념에 기초를 두고 있습니다.
- 명령어 모드 - vim은 이 모드로 시작됩니다. 이동과 명령어 입력에 사용합니다.
- 삽입 모드 - 파일을 수정합니다.
- 비주얼 모드 - 텍스트를 하이라이트하고 그 텍스트에 대한 작업을 합니다.
- 실행 모드 - ':' 이후 명령어를 입력합니다.
```
i # 커서 위치 앞에서 삽입 모드로 변경
a # 커서 위치 뒤에서 삽입 모드로 변경
v # 비주얼 모드로 변경
: # 실행 모드로 변경
<esc> # 현재 모드를 벗어나 명령어 모드로 변경
# 복사와 붙여넣기
y # 선택한 객체 복사(Yank)
yy # 현재 줄 복사
d # 선택한 객체 삭제
dd # 현재 줄 삭제
p # 커서 위치 뒤에 복사한 텍스트 붙여넣기
P # 커서 위치 앞에 복사한 텍스트 붙여넣기
x # 현재 커서 위치의 문자 삭제
```
## vim의 '문법'
Vim의 명령어는 '서술어-수식어-목적어'로 생각할 수 있습니다.
서술어 - 취할 동작
수식어 - 동작을 취할 방식
목적어 - 동작을 취할 객체
'서술어', '수식어', '목적어'의 예시는 다음과 같습니다.
```
# '서술어'
d # 지운다
c # 바꾼다
y # 복사한다
v # 선택한다
# '수식어'
i # 안에
a # 근처에
NUM # (숫자)
f # 찾아서 그곳에
t # 찾아서 그 앞에
/ # 문자열을 커서 뒤로 찾아서
? # 문자열을 커서 앞으로 찾아서
# '목적어'
w # 단어를
s # 문장을
p # 문단을
b # 블록을
# 예시 '문장' (명령어)
d2w # 단어 2개를 지운다
cis # 문장 안을 바꾼다
yip # 문단 안을 복사한다
ct< # 여는 괄호까지 바꾼다
# 현재 위치에서 다음 여는 괄호까지의 텍스트를 바꾼다
d$ # 줄 끝까지 지운다
```
## 몇 가지 트릭
<!--TODO: Add more!-->
```
> # 선택한 영역 한 칸 들여쓰기
< # 선택한 영역 한 칸 내어쓰기
:earlier 15m # 15분 전의 상태로 되돌리기
:later 15m # 위의 명령어를 취소
ddp # 이어지는 줄과 위치 맞바꾸기 (dd 후 p)
. # 이전 동작 반복
:w !sudo tee % # 현재 파일을 루트 권한으로 저장
:set syntax=c # 문법 강조를 'C'의 것으로 설정
:sort # 모든 줄을 정렬
:sort! # 모든 줄을 역순으로 정렬
:sort u # 모든 줄을 정렬하고, 중복되는 것을 삭제
~ # 선택된 텍스트의 대/소문자 토글
u # 선택된 텍스트를 소문자로 바꾸기
U # 선택된 텍스트를 대문자로 바꾸기
# 텍스트 폴딩
zf # 선택된 텍스트 위치에서 폴딩 만들기
zo # 현재 폴딩 펴기
zc # 현재 폴딩 접기
zR # 모든 폴딩 펴기
zM # 모든 폴딩 접기
zi # 폴딩 접기/펴기 토글
zd # 접은 폴딩 삭제
```
## 매크로
매크로는 기본적으로 녹화할 수 있는 동작을 말합니다.
매크로를 녹화하기 시작하면, 끝날 때까지 **모든** 동작과 명령어가 녹화됩니다.
매크로를 호출하면 선택한 텍스트에 대해 정확히 같은 순서의 동작과 명령어가 실행됩니다.
```
qa # 'a'라는 이름의 매크로 녹화 시작
q # 녹화 중지
@a # 매크로 실행
```
### ~/.vimrc 설정
.vimrc 파일은 Vim이 시작할 때의 설정을 결정합니다.
다음은 ~/.vimrc 파일의 예시입니다.
```vim
" ~/.vimrc 예시
" 2015.10
" vim이 iMprove 되려면 필요
set nocompatible
" 자동 들여쓰기 등을 위해 파일 명으로부터 타입 결정
filetype indent plugin on
" 신택스 하이라이팅 켜기
syntax on
" 커맨드 라인 완성 향상
set wildmenu
" 대문자를 썼을 때가 아니면 대소문자 구분하지 않고 검색
set ignorecase
set smartcase
" 줄넘김을 했을 때 파일에 따른 들여쓰기가 켜져 있지 않다면
" 현재 줄과 같은 들여쓰기를 유지
set autoindent
" 좌측에 줄 번호 표시
set number
" 들여쓰기 설정 (개인 기호에 따라 변경)
" 탭 하나와 시각적으로 같을 스페이스 개수
set tabstop=4
" 편집할 때 탭 하나에 들어갈 스페이스 수
set softtabstop=4
" 들여쓰기 혹은 내어쓰기 작업(>>, <<)을 했을 때 움직일 스페이스 개수
set shiftwidth=4
" 탭을 스페이스로 변환
set expandtab
" 들여쓰기와 정렬에 자동 탭 및 스페이스 사용
set smarttab
```
### 참고 자료
[(영어) Vim 홈페이지](http://www.vim.org/index.php)
`$ vimtutor`
[(영어) vim 입문과 기초](https://danielmiessler.com/study/vim/)
[(영어) 엄마가 말해주지 않은 Vim의 어두운 구석들 (Stack Overflow 게시물)](http://stackoverflow.com/questions/726894/what-are-the-dark-corners-of-vim-your-mom-never-told-you-about)
[(영어) 아치 리눅스 위키](https://wiki.archlinux.org/index.php/Vim)

168
ko/xml.md Normal file
View File

@@ -0,0 +1,168 @@
---
language: XML
filename: learnxml-kr.xml
contributors:
- ["João Farias", "https://github.com/JoaoGFarias"]
- ["Rachel Stiyer", "https://github.com/rstiyer"]
- ["Deepanshu Utkarsh", "https://github.com/duci9y"]
translators:
- ["Wooseop Kim", "https://github.com/linterpreteur"]
lang: ko-kr
---
XML은 데이터를 저장하고 전송하기 위해 설계된 마크업 언어입니다. 인간과 기계 모두가 읽을 수 있도록 만들어졌습니다.
XML은 HTML과는 달리 데이터를 보여주는 방법이나 그 형식을 특정하지 않습니다. 단지 데이터를 담을 뿐입니다.
차이는 **내용**과 **마크업**에 있습니다. 내용은 무엇이든 될 수 있지만, 마크업은 정의되어 있습니다.
## 기초 정의 및 도입
XML 문서는 기본적으로 자신을 설명하는 *속성*을 가질 수 있으며 자식으로서 텍스트 혹은 다른 요소를 가질 수 있는 *요소*들로 이루어집니다. 모든 XML 문서는 반드시 루트 요소를 가져야 합니다. 루트 요소는 문서에 있는 모든 다른 요소들의 조상입니다.
XML 파서는 매우 엄격하게 설계되어 있으므로 문서의 형식이 틀렸다면 파싱을 멈출 것입니다. 그러므로 모든 XML 문서는 [(영어) XML 문법 규칙](http://www.w3schools.com/xml/xml_syntax.asp)을 따른다고 보장할 수 있습니다.
```xml
<!-- 주석에는 두 개의 연속된 하이픈(-)이 들어갈 수 없습니다. -->
<!-- 주석은 여러 줄로
이어질 수 있습니다. -->
<!-- 요소 -->
<!-- 요소는 XML의 기본적 구성품입니다. 요소에는 두 개의 유형이 있습니다. -->
<element1 attribute="value" /> <!-- 빈 요소는 내용을 담지 않습니다. -->
<!-- 그리고 비지 않은 요소가 있습니다. -->
<element2 attribute="value">내용</element2>
<!-- 요소 이름에는 알파벳과 숫자만이 허용됩니다. -->
<empty /> <!-- 요소는 어떠한 내용도 없이 순수한 마크업인 -->
<!-- 빈 요소 태그로 구성될 수 있습니다. -->
<notempty> <!-- 혹은 여는 태그와 -->
<!-- 내용, -->
</notempty> <!-- 그리고 닫는 태그로 구성될 수도 잇습니다. -->
<!-- 요소 이름은 대소문자를 구별합니다. -->
<element />
<eLEMENT />
<!-- 둘은 같지 않습니다. -->
<!-- 속성 -->
<!-- 속성은 요소 안에 존재하는 키와 값의 쌍입니다. -->
<element attribute="value" another="anotherValue" many="space-separated list" />
<!-- 속성은 원소에서 단 한 번만 나타날 수 있습니다. 속성은 단 하나의 값만 갖습니다.
이에 대한 흔한 해결책은 공백으로 구분된 리스트를 포함하는 것입니다. -->
<!-- 중첩 요소 -->
<!-- 한 요소의 내용은 다른 요소들을 포함할 수 있습니다. -->
<parent>
<child>Text</child>
<emptysibling />
</parent>
<!-- 표준적인 트리 명칭이 사용됩니다. 각각의 요소는 노드라고 부릅니다.
한 단계 위의 조상은 부모이며, 한 단계 아래의 후손은 자식입니다.
같은 부모 요소를 가진 요소들은 자매입니다. -->
<!-- XML은 공백을 보존합니다. -->
<child>
Text
</child>
<child>Text</child>
<!-- 둘은 같지 않습니다. -->
```
## XML 문서
XML이 유용한 것은 인간도 읽을 수 있다는 것입니다. 다음의 문서는 에릭 레이의 XML 배우기를 포함해 세 권의 책을 파는 서점을 정의한다는 것을 알 수 있습니다. XML 파서 없이도 이렇게 쉽습니다.
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- XML 프롤로그라는 것입니다. 필수는 아니지만, 권장됩니다. -->
<bookstore>
<book category="COOKING">
<title lang="ko">매일 이탈리아 요리</title>
<author>지아다 데 라우렌티스</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="CHILDREN">
<title lang="ko">해리 포터</title>
<author>J K 롤링</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="WEB">
<title lang="ko">XML 배우기</title>
<author>에릭 레이</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
```
## 적격성과 유효성
XML 문서는 문법적으로 정확할 경우 *적격*합니다. 하지만 문서 유형 정의(DTD)를 이용하여 문서에 제약을 더 추가할 수 있습니다. 한 문서의 요소와 속성이 DTD 안에 정의되어 있고 그 파일에 특정된 문법을 따른다면 *적격*할 뿐만 아니라 그 DTD에 대하여 *유효*하다고 말합니다.
```xml
<!-- DTD를 외부에 선언: -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookstore SYSTEM "Bookstore.dtd">
<!-- bookstore가 루트 요소이며 'Bookstore.dtd'가 DTD 파일의
경로임을 선언합니다. -->
<bookstore>
<book category="COOKING">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
</bookstore>
<!-- DTD 파일 -->
<!ELEMENT bookstore (book+)>
<!-- bookstore 요소는 하나 이상의 book 요소를 자식으로 가질 수 있습니다. -->
<!ELEMENT book (title, price)>
<!-- 각각의 book은 title과 price를 자식으로 반드시 갖습니다. -->
<!ATTLIST book category CDATA "Literature">
<!-- book은 category 속성을 가져야 합니다. 그렇지 않다면 그 기본값은 'Literature'입니다. -->
<!ELEMENT title (#PCDATA)>
<!-- title 요소는 반드시 PCDATA만 포함해야 합니다. 즉,
파서가 읽을 텍스트만을 포함해야 하며 자식을 포함할 수 없습니다.
CDATA와 비교해 보세요. -->
<!ELEMENT price (#PCDATA)>
]>
<!-- DTD는 XML 파일 안에 선언될 수도 있습니다. -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE bookstore [
<!ELEMENT bookstore (book+)>
<!ELEMENT book (title, price)>
<!ATTLIST book category CDATA "Literature">
<!ELEMENT title (#PCDATA)>
<!ELEMENT price (#PCDATA)>
]>
<bookstore>
<book category="COOKING">
<title>Everyday Italian</title>
<price>30.00</price>
</book>
</bookstore>
```
## DTD 호환성과 XML 스키마 정의
DTD는 오래되었기 때문에 지원이 광범위합니다. 불행히도 네임스페이스와 같은 현대적 XML 기능은 DTD에서 지원하지 않습니다. XML 스키마 정의(XSD)가 XML 문서의 문법을 정의하기 위한 DTD의 대체재입니다.
## Resources
* [(영어) Validate your XML](http://www.xmlvalidation.com)
## Further Reading
* [(영어) XML 스키마 정의 튜토리얼](http://www.w3schools.com/xml/xml_schema.asp)
* [(영어) DTD 튜토리얼](http://www.w3schools.com/xml/xml_dtd_intro.asp)
* [(영어) XML 튜토리얼](http://www.w3schools.com/xml/default.asp)
* [(영어) XPath 쿼리로 XML 파싱하기](http://www.w3schools.com/xml/xml_xpath.asp)

185
ko/yaml.md Normal file
View File

@@ -0,0 +1,185 @@
---
language: YAML
filename: learnyaml-kr.yaml
contributors:
- ["Leigh Brenecki", "https://github.com/adambrenecki"]
- ["Suhas SG", "https://github.com/jargnar"]
translators:
- ["Wooseop Kim", "https://github.com/linterpreteur"]
- ["Justin Yang", "https://github.com/justin-themedium"]
lang: ko-kr
---
YAML은 인간이 직접 쓰고 읽을 수 있도록 설계된 데이터 직렬화 언어입니다.
YAML은 마치 파이썬처럼 개행과 들여쓰기에 문법적으로 의미를 준 JSON의 엄격한 수퍼셋입니다.
하지만 파이썬과는 달리 YAML은 탭 문자를 들여쓰기에 사용하지 않습니다.
```yaml
--- # 문서 시작
# YAML의 주석은 이런 식입니다.
############
# 스칼라 형 #
############
# 문서 내내 이어질 루트 객체는 맵입니다.
# 맵은 다른 언어의 딕셔너리, 해시, 혹은 객체에 해당합니다.
:
다른_키: 다른 값이 여기 옵니다.
숫자_값: 100
과학적_표기법: 1e+12
# 숫자 1은 불리언이 아닌 값으로 처리됩니다. 불리언으로 처리하고 싶다면
# true를 사용하세요.
불리언: true
널_값: null
띄어서 쓴 키:
# 문자열에 따옴표를 칠 필요는 없습니다. 하지만 칠 수도 있습니다.
하지만: '따옴표에 담은 문자열'
'키도 따옴표에 담을 수 있습니다.': "키에 ':'을 넣고 싶다면 유용합니다."
작은 따옴표: '는 ''하나''의 이스케이프 패턴을 갖습니다'
큰 따옴표: "는 많이 갖습니다. \", \0, \t, \u263A, \x0d\x0a == \r\n, 그리고 더."
# UTF-8/16/32 문자는 인코딩되어야 합니다.
첨자 2: \u00B2
# 여러 줄의 문자열은 (|을 이용한) '리터럴 블락' 혹은 (>을 이용한) '접은 블락'으로
# 쓸 수 있습니다.
리터럴_블락: |
개행을 포함한 이 모든 덩어리가 '리터럴_블락' 키에 대응하는 값이 될 것입니다.
리터럴 값은 들여쓰기가 끝날 때까지 계속되며 들여쓰기는 문자열에 포함되지
않습니다.
'들여쓰기를 더 한' 줄은 나머지 들여쓰기를 유지합니다.
이 줄은 띄어쓰기 4개만큼 들여쓰기 됩니다.
접는_방식: >
이 텍스트 덩어리가 전부 '접는_방식' 키의 값이 되지만, 이번에는 모든 개행 문자가
띄어쓰기 하나로 대체됩니다.
위와 같이 텅 빈 줄은 개행 문자로 바뀝니다.
'더 들여쓴' 줄 역시 개행 문자를 유지합니다.
이 텍스트는 두 줄에 걸쳐 나타날 것입니다.
##########
# 모임 형 #
##########
# 중첩은 들여쓰기를 사용합니다. 2칸 띄어쓰기가 많이 쓰입니다(필수는 아닙니다).
중첩된_맵:
:
다른_키: 다른 값
다른_중첩된_맵:
안녕: 안녕
# 맵은 반드시 문자열 키를 가지는 것은 아닙니다.
0.25: 실수형 키
# 키는 여러 줄에 걸친 객체와 같이 복합적일 수도 있습니다.
# ?와 그 뒤의 띄어쓰기로 복합 키의 시작을 나타냅니다.
? |
여러 줄짜리
: 그리고 그 값
# YAML은 복합 키 문법으로 연속열 간의 매핑도 지원합니다.
# 일부 파서는 지원하지 않을 수 있습니다.
# 예시
? - 맨체스터 유나이티드
- 레알 마드리드
: [2001-01-01, 2002-02-02]
# 리스트 혹은 배열에 대응되는 연속열은 다음과 같습니다.
# (들여쓰기처럼 '-'를 세는 것에 주의하세요)
연속열:
- 하나
-
- 0.5 # 연속열은 다른 형을 포함 가능
-
- :
다른_키: 다른_값
-
- 연속열 안의
- 또 다른 연속열
- - - 중첩된 연속열 지시자
- 접힘 가능
# YAML은 JSON의 수퍼셋이기 때문에, JSON식으로 맵과 연속열을 작성할 수도
# 있습니다.
제이슨_맵: {"키": "값"}
제이슨_열: [3, 2, 1, "발사"]
#################
# 기타 YAML 기능 #
#################
# YAML은 '앵커'라는 편리한 기능이 있습니다. 앵커를 이용하면 문서에서
# 손쉽게 내용을 복제할 수 있습니다. 이 키들은 같은 값을 갖습니다.
앵커된_내용: &앵커_이름 이 문자열은 두 키의 값으로 나타납니다.
다른_앵커: *앵커_이름
# 앵커는 속성을 복제하거나 상속할 수 있습니다.
기반: &기반
이름: 모두 이름이 같다
# 정규식 << 는 병합 키 언어-비종속 타입으로 불립니다. 이는 하나
# 이상 지정된 맵의 모든 키가 현재 맵 안으로 삽입됨을 나타냅니다.
멍멍:
<<: *기반
나이: 10
야옹:
<<: *기반
나이: 20
# 멍멍이와 야옹이도 '이름: 모두 이름이 같다'를 갖습니다.
# 또한 YAML에는 명시적으로 형을 선언할 수 있는 태그가 있습니다.
명시적_문자열: !!str 0.5
# 파이썬의 복소수 형을 나타내는 다음 태그처럼, 일부 파서는 언어에 종속된 태그를
# 구현합니다.
파이썬_복소수: !!python/complex 1+2j
# YAML 복합 키를 언어 종속 태그와 함께 사용할 수도 있습니다.
? !!python/tuple [5, 7]
: 오십칠
# 파이썬에서의 {(5, 7): '오십칠'} 객체
###############
# 기타 YAML 형 #
###############
# YAML이 이해할 수 있는 스칼라가 문자열과 수만 있는 것은 아닙니다.
# ISO 형식 날짜와 시간 리터럴 또한 해석됩니다.
시간: 2001-12-15T02:59:43.1Z
띄어쓰기_한_시간: 2001-12-14 21:59:43.10 -5
날짜: 2002-12-14
# !!binary 태그는 문자열이 실제로는 base64로 인코딩된
# 이진수 객체(BLOB)라는 것을 나타냅니다.
이미지_파일: !!binary |
R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
+f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=
# YAML에는 다음과 같은 집합도 있습니다.
집합:
? 하나
?
?
# 집합은 단지 널 값을 갖는 맵입니다. 위는 다음과 같습니다.
집합2:
하나: null
: null
: null
... # 문서 끝
```
### 더 읽기
+ [(영어) YAML 공식 사이트](https://yaml.org/)
+ [(영어) 온라인 YAML 검사기](http://www.yamllint.com/)