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:
381
ko/bash.md
Normal file
381
ko/bash.md
Normal 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
85
ko/bf.md
Normal 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
147
ko/clojure-macros.md
Normal 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
383
ko/clojure.md
Normal 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
58
ko/coffeescript.md
Normal 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
684
ko/common-lisp.md
Normal 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
332
ko/erlang.md
Normal 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
345
ko/go.md
Normal 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
407
ko/java.md
Normal 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
430
ko/javascript.md
Normal 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
80
ko/json.md
Normal 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
375
ko/kotlin.md
Normal 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
421
ko/lua.md
Normal 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
325
ko/markdown.md
Normal 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
|
||||

|
||||
```
|
||||
|
||||
참조 방식도 가능합니다.
|
||||
|
||||
```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
661
ko/php.md
Normal 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
479
ko/pythonlegacy.md
Normal 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
640
ko/racket.md
Normal 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
269
ko/vim.md
Normal 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
168
ko/xml.md
Normal 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
185
ko/yaml.md
Normal 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/)
|
Reference in New Issue
Block a user