diff --git a/ko/ada.md b/ko/ada.md new file mode 100644 index 0000000000..d31cd75d0e --- /dev/null +++ b/ko/ada.md @@ -0,0 +1,398 @@ +--- +name: Ada +filename: learn.ada +contributors: + - ["Luke A. Guest", "https://github.com/Lucretia"] + - ["Fernando Oleo Blanco", "https://github.com/Irvise"] + - ["Fabien Chouteau", "https://github.com/Fabien-Chouteau"] + - ["Manuel", "https://github.com/mgrojo"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Ada는 파스칼/알골 계열의 강력한 정적 타입 명령형, [객체 지향](https://ada-lang.io/docs/arm/AA-3/AA-3.9), [실시간](https://ada-lang.io/docs/arm/AA-D), [병렬](https://ada-lang.io/docs/arm/AA-9) 및 [분산](https://ada-lang.io/docs/arm/AA-9) 프로그래밍 언어이지만, 오늘날에는 ```begin/end``` 키워드 쌍, ```:=``` 할당 기호, 레코드 및 ```if/case``` 제어문 구조만 남아 파스칼과 약간의 유사성만 가집니다. + +Ada는 원래 [객체 기반](https://ada-lang.io/docs/arm/AA-3/AA-3.3) 언어로 설계되었으며 미국 정부에서 사용하는 수백 개의 언어를 대체하기 위한 것이었습니다. 이는 모든 엔티티가 객체 지향적 의미가 아닌 객체라는 것을 의미합니다. 이 언어는 1995년에 [객체 지향](https://ada-lang.io/docs/arm/AA-3/AA-3.9)이 되었고, 2005년에는 Java에서 파생된 [인터페이스](https://ada-lang.io/docs/arm/AA-3/AA-3.9#Subclause_3.9.4)를 추가했습니다. [계약 기반](https://ada-lang.io/docs/arm/AA-13/AA-13.1#Subclause_13.1.1) 프로그래밍은 Ada 2012와 함께 도입되었습니다. + +Ada는 프로그래머가 아닌 사람(예: 조직 내 관리자)도 쉽게 읽고 배울 수 있도록 설계되었으므로 이 언어로 작성된 프로그램은 약간 더 장황한 경향이 있습니다. + +Ada는 현대적인 프로그래밍 언어이며, 이제 다른 현대 언어와 마찬가지로 Alire라는 패키지 관리자가 있습니다. 아래를 참조하십시오. + +```ada +-- 주석은 이중 하이픈으로 작성되며 줄 끝까지 존재합니다. + +-- 진입점을 "Main" 또는 "main"으로 호출할 필요는 없으며, 프로그램이 +-- 수행하는 작업에 따라 이름을 지정해야 합니다. +procedure Empty is + -- 이것은 선언부입니다. +begin + -- 여기에 문장이 들어갑니다. + null; -- 여기서는 아무것도 하지 않습니다. +end Empty; + +-- Ada 컴파일러는 라이브러리 패키지, 태스크, 하위 프로그램, 제네릭 등이 될 수 있는 +-- 컴파일 단위를 허용합니다. + +-- 여기에 "컨텍스트 절"이 들어갑니다. 이것은 pragma 또는 "with" 문이 될 수 있습니다. +-- "with"는 다른 언어의 "include" 또는 "import"와 동일합니다. +with Ada.Text_IO; -- 라이브러리 패키지에 대한 접근 권한을 얻습니다. + +procedure Hello is +begin + Ada.Text_IO.Put_Line ("Hello, world"); + + Ada.Text_IO.Put ("Hello again, world"); + Ada.Text_IO.New_Line; +end Hello; + + +-- Ada에는 실제 모듈 시스템이 있습니다. 모듈은 패키지라고 불리며 +-- 사양과 본문이라는 두 가지 구성 요소로 나뉩니다. +-- 처음부터 패키지를 사용하게 되므로 패키지를 일찍 소개하는 것이 중요합니다. +package Stuff is + -- 이 패키지가 "main" 프로시저가 시작되기 전에 코드를 실행할 필요가 없다고 + -- 컴파일러에 알리기 위해 다음 줄을 추가할 수 있습니다. + -- pragma Preelaborate; + + -- 패키지는 동일한 파일 내에서 또는 외부에서 중첩될 수 있습니다. + -- 중첩된 패키지는 점 표기법(예: Stuff.Things.My)을 통해 접근합니다. + package Things is + My : constant Integer := 100; + end Things; + + -- 사양 내에 하위 프로그램이 선언된 경우, 하위 프로그램의 본문은 + -- 패키지 본문 내에 선언되어야 합니다. + procedure Do_Something; -- 하위 프로그램이 매개변수를 받지 않으면 다른 언어와 달리 + -- 빈 괄호가 필요하지 않습니다. + + -- 제네릭 하위 프로그램을 만들 수도 있습니다. + generic + type Element is (<>); -- "(<>)" 표기법은 이산 유형만 + -- 제네릭에 전달될 수 있음을 지정합니다. + procedure Swap (Left, Right : in out Element); + + -- 때로는 외부 세계에서 유형이 어떻게 정의되었는지 숨기고 싶을 때가 있습니다. + -- 아무도 직접 건드릴 수 없도록 말이죠. 전체 유형은 아래의 private 섹션에 + -- 정의되어야 합니다. + type Blobs is private; + + -- "is" 키워드 뒤에 이 키워드를 넣어 유형을 "제한"할 수도 있습니다. + -- 이는 사용자가 일반적으로 할 수 있는 것처럼 해당 유형의 객체를 + -- 복사할 수 없음을 의미합니다. +private + type Blobs is new Integer range -25 .. 25; +end Stuff; + + +package body Stuff is + -- 하위 프로그램 본문. + procedure Do_Something is + -- 하위 프로그램을 중첩할 수도 있습니다. + -- 매개변수는 이동 방향(in, in out, out)으로 정의됩니다. + -- 이동 방향이 지정되지 않으면 기본적으로 in입니다. + function Times_4 (Value : in Integer) return Integer is + begin + return Value * 4; + end Times_4; + + I : Integer := 4; + begin + I := Times_4 (I); + end Do_Something; + + + -- 제네릭 프로시저 본문. + procedure Swap (Left, Right : in out Element) is + Temp : Element := Left; + begin + Left := Right; + Right := Temp; + end Swap; +begin + -- 패키지 내에서 무언가를 초기화해야 하는 경우 여기에서 할 수 있습니다. + Do_Something; +end Stuff; + + +with Ada.Unchecked_Conversion; +with Ada.Text_IO; +with Stuff; + +procedure LearnAdaInY is + -- 들여쓰기는 3칸입니다. + + -- Ada에서 가장 중요한 기능은 유형입니다. 객체에는 유형이 있으며 + -- 한 유형의 객체는 다른 유형의 객체에 할당될 수 없습니다. + + -- 모델링하는 도메인에 대해 자신만의 유형을 정의할 수 있으며, 그렇게 해야 합니다. + -- 하지만 표준 유형으로 시작한 다음 나중에 자신만의 유형으로 바꿀 수 있습니다. + -- 이것은 점진적 타이핑의 한 형태라고 할 수 있습니다. + + -- 표준 유형은 C와 같은 다른 언어에 바인딩하기 위한 좋은 출발점일 뿐입니다. + -- Ada는 C, Fortran 및 COBOL과 바인딩하는 표준화된 방법을 가진 유일한 언어입니다! + -- 이러한 언어에 대한 바인딩에 대한 자세한 내용은 참고 자료 섹션의 링크를 참조하십시오. + + type Degrees is range 0 .. 360; -- 이것은 유형입니다. 기본 표현은 + -- 정수입니다. + + type Hues is (Red, Green, Blue, Purple, Yellow); -- 이것도 마찬가지입니다. 여기서는 + -- 열거형을 선언하고 있습니다. + + -- 이것은 모듈러 유형입니다. 자동으로 랩어라운드되는 정수처럼 동작합니다. + -- 이 특정 경우의 범위는 0 .. 359입니다. + -- 값 359를 포함하는 변수에 1을 더하면 0을 받게 됩니다. + -- 배열에 매우 유용합니다. + type Degrees_Wrap is mod 360; + + -- 하위 유형을 사용하여 유형의 범위를 제한할 수 있습니다. 이렇게 하면 서로 호환됩니다. + -- 즉, 아래에서 볼 수 있듯이 하위 유형을 유형의 객체에 할당할 수 있습니다. + subtype Primaries is Hues range Red .. Blue; -- 이것은 범위입니다. + + -- 다음과 같이 변수나 상수를 정의할 수 있습니다. + -- Var_Name : Type := Value; + + -- 10은 보편적인 정수입니다. 이러한 보편적인 숫자는 기본 유형과 일치하는 + -- 모든 유형과 함께 사용할 수 있습니다. + Angle : Degrees := 10; + Value : Integer := 20; + -- New_Angle : Degrees := Value; -- 호환되지 않는 유형은 컴파일되지 않습니다. + -- New_Value : Integer := Angle; + + Blue_Hue : Primaries := Blue; -- 변수. + Red_Hue : constant Primaries := Red; -- 상수. + Yellow_Hue : constant Hues := Yellow; + Colour_1 : constant Hues := Red_Hue; + -- Colour_2 : constant Primaries := Yellow_Hue; -- 컴파일하려면 주석을 해제하십시오. + + -- 변환을 강제할 수 있지만, 그러면 패키지 이름으로 안전하지 않은 작업을 + -- 수행하고 있을 수 있다는 경고를 받게 됩니다. + function Degrees_To_Int is new Ada.Unchecked_Conversion + (Source => Degrees, -- 줄 연속은 2칸 들여쓰기됩니다. + Target => Integer); + + New_Value_2 : Integer := Degrees_To_Int (Angle); -- 참고, ( 앞에 공백. + + -- GNAT는 GNU Ada 번역기(컴파일러)입니다. + -- Ada에는 스타일 가이드가 있으며 GNAT는 이를 준수하도록 경고하고, + -- 모든 Ada 소스가 일관되게 보이도록 스타일을 확인하는 옵션이 있습니다. + -- 그러나 스타일은 사용자 정의할 수 있습니다. + + -- 예, 자신만의 부동 소수점 및 고정 소수점 유형을 정의할 수도 있습니다. + -- 이것은 매우 드물고 독특한 능력입니다. "digits"는 유형이 지원해야 하는 + -- 최소 자릿수 정밀도를 나타냅니다. "delta"는 고정 소수점 유형에 대한 것이며 + -- 유형이 지원할 가장 작은 변경을 나타냅니다. + type Real_Angles is digits 3 range 0.0 .. 360.0; + type Fixed_Angles is delta 0.01 digits 5 range 0.0 .. 360.0; + + RA : constant Real_Angles := 36.45; + FA : constant Fixed_Angles := 360.0; -- 부동 소수점으로 만들기 위해 ".0". + + -- 기본적으로 일반적인 라틴 1 기반 문자열을 가질 수 있습니다. + Str : constant String := "This is a constant string"; + -- 문자열 리터럴에서 초기화할 때 컴파일러는 범위를 알고 있으므로 + -- 정의할 필요가 없습니다. + + -- 문자열은 배열입니다. 괄호를 사용하여 배열의 요소에 접근하는 방법을 + -- 보셨습니까? 이것은 수학적 표기법이며 Ada가 만들어질 당시 모든 키보드에서 + -- 대괄호를 사용할 수 없었기 때문에 사용되었습니다. 또한 배열은 수학적 관점에서 + -- 함수로 볼 수 있으므로 배열과 함수 간의 변환이 더 쉬워졌습니다. + Char : constant Character := Str (Str'First); -- "'First"는 유형 속성입니다. + + -- Ada 2022에는 Ada 2012에 추가된 컨테이너를 사용할 때 배열 초기화를 위해 + -- []를 사용하는 것이 포함됩니다. + + -- 배열은 일반적으로 항상 유형으로 정의됩니다. + -- 어떤 차원이든 될 수 있습니다. + type My_Array_1 is array (1 .. 4, 3 .. 7, -20 .. 20) of Integer; + + -- 예, 다른 언어와 달리 열거형 및 모듈러 유형 또는 임의의 범위와 같은 + -- 다른 이산 유형으로 배열을 인덱싱할 수 있습니다. + type Axes is (X, Y, Z); + + -- 다른 유형의 'Range 속성을 사용하여 배열의 범위를 정의할 수 있습니다. + type Vector is array (Axes'Range) of Float; + + V1 : constant Vector := (0.0, 0.0, 1.0); + + -- 레코드는 C, C++의 구조체와 동일합니다. + type Entities is record + Name : String (1 .. 10); -- 항상 양수 값에서 시작하며, + -- 포함 범위입니다. + Position : Vector; + end record; + + -- Ada에서 배열 범위는 변경할 수 없습니다. 따라서 모든 문자에 대해 + -- 값이 있는 문자열 리터럴을 제공해야 합니다. + E1 : constant Entities := ("Blob ", (0.0, 0.0, 0.0)); + + -- 대안은 배열 집계를 사용하고 이 집계에서 이전에 할당되지 않은 + -- 모든 요소에 기본값을 할당하는 것입니다. + -- "others"는 명시적으로 초기화되지 않은 다른 모든 것을 나타내는 데 사용됩니다. + E2 : constant Entities := (('B', 'l', 'o', 'b', others => ' '), + (0.0, 0.0, 0.0)); + + -- 표준 라이브러리에는 동적 길이 문자열(참고 자료 섹션 참조)이 있습니다. + + -- 상자 표기법 "<>"을 사용하여 객체를 기본값으로 초기화할 수 있습니다. + -- "others"는 명시적으로 초기화되지 않은 다른 모든 것을 나타내는 데 사용됩니다. + Null_Entity : constant Entities := (others => <>); + + -- 객체 지향은 레코드 구문의 확장인 태그가 지정된 레코드를 통해 수행됩니다. + -- 위의 첫 번째 단락에 있는 링크를 참조하십시오. + + -- 가독성을 높이기 위해 객체 이름(별칭)을 바꿀 수 있습니다. + package IO renames Ada.Text_IO; +begin + -- 열거형을 이름으로 출력할 수 있습니다. + IO.Put_Line ("Blue_Hue = " & -- &는 문자열 연결 연산자입니다. + Blue'Image); -- '는 객체의 속성에 접근합니다. + -- Image 속성은 값을 문자열로 변환합니다. + -- Ada 2022는 Image를 사용자 정의 유형으로 확장했습니다. + -- -gnat2022 컴파일러 플래그로 이것에 접근하십시오. + IO.Put_Line ("Yellow_Hue = " & + -- 유형의 속성을 사용할 수 있습니다. + Primaries'Image (Yellow_Hue)); + + -- 선언 블록 내에 지역 변수를 정의할 수 있으며, 레이블을 지정하여 + -- 더 읽기 쉽게 만들 수 있습니다. + Enum_IO : declare + package Hue_IO is new IO.Enumeration_IO (Hues); + + -- 패키지를 사용하면 해당 패키지 내의 모든 것이 이 블록 내에서 + -- 보이게 됩니다. 이것을 컨텍스트 절 내의 전체 패키지가 아닌 + -- 로컬에서만 수행하는 것이 좋습니다. + use Hue_IO; + begin + -- 열거형 값도 출력할 수 있습니다. + Put (Purple); -- Put 프로시저 앞에 Hue_IO를 붙일 필요가 없습니다. + IO.New_Line; -- 여기서는 여전히 IO를 접두사로 붙여야 합니다. + Put (Red_Hue); + IO.New_Line; + end Enum_IO; + + -- 루프는 일관된 형태를 가집니다. "
loop ... end loop". + -- 여기서 "form"은 "while" 또는 "for"이거나 아래와 같이 없을 수 있습니다. + -- "loop ... end loop;" 구문을 자체 줄에 배치하면 + -- 다른 루프 구문을 더 쉽게 주석 처리하거나 실험할 수 있습니다. + declare + Counter : Positive := Positive'First; -- 이것은 1입니다. + begin + -- 필요한 경우 더 쉽게 빠져나올 수 있도록 루프에 레이블을 지정할 수 있습니다. + Infinite : + loop + IO.Put_Line ("[Infinite loop] Counter = " & Counter'Image); + + Counter := Counter + 1; + + -- 다음 줄은 repeat ... until 또는 do ... while 루프 구문을 구현합니다. + -- 무한 루프를 위해 주석 처리하십시오. + exit Infinite when Counter = 5; -- 등호 테스트는 단일 "="를 사용합니다. + end loop Infinite; -- 상태 머신을 구현할 때 유용합니다. + end; + + declare -- 레이블이 없어도 됩니다. + Counter : Positive := Positive'First; -- 이것은 1입니다. + begin + while Counter < 10 + loop + IO.Put_Line ("Counter = " & Counter'Image); + + Counter := Counter + 1; -- 명시적인 증감 연산자는 없습니다. + + -- Ada 2022는 LHS에 @를 도입했으므로 위는 다음과 같이 작성됩니다. + -- Counter := @ + 1; -- -gnat2022로 시도해보십시오. + end loop; + end; + + declare + package Hue_IO is new IO.Enumeration_IO (Hues); + + -- 한 줄에 여러 패키지를 가질 수 있지만, 가독성을 위해 + -- 한 줄에 하나의 패키지를 사용하는 경향이 있습니다. + use IO, Hue_IO; + begin + Put ("Hues : "); -- 참고, 접두사 없음. + + -- 'Range 속성을 사용하고 있기 때문에 컴파일러는 이것이 안전하다는 것을 + -- 알고 있으며 여기서 런타임 검사를 생략할 수 있습니다. + for Hue in Hues'Range + loop + Put (Hue); + + -- 유형과 객체는 자신의 범위, 즉 First .. Last 값을 알고 있습니다. + -- 이것은 범위 유형으로 지정할 수 있습니다. + if Hue /= Hues'Last then -- /=는 수학 기호 ≠와 같이 "같지 않음"을 의미합니다. + Put (", "); + end if; + end loop; + + IO.New_Line; + end; + + -- 문자열을 포함한 모든 객체는 자신의 범위를 알고 있습니다. + declare + C : Character := Str (50); -- 경고가 발생하고 런타임에 예외가 발생합니다. + -- + -- 위에서 발생한 예외는 외부 범위에서만 처리할 수 있습니다. + -- 아래의 위키북 링크를 참조하십시오. + begin + null; -- 위의 이유로 이 지점에는 절대 도달하지 못합니다. + end; +exception + when Constraint_Error => + IO.Put_Line ("Caught the exception"); +end LearnAdaInY; +``` + +이제 Ada에 대한 기본 소개를 위해 많은 정보를 다루었지만, 아직 표면만 훑었을 뿐입니다. 아래 참고 자료 섹션에는 더 많은 내용이 있습니다. 동적 메모리 할당에 대해서는 아직 다루지 않았는데, 여기에는 [풀](https://ada-lang.io/docs/arm/AA-13/AA-13.11)이 포함됩니다. 이는 대부분의 Ada 프로그램이 필요로 하지 않기 때문이며, 그것 없이도 많은 것을 할 수 있습니다. + +위에서 언급했듯이, Ada는 파스칼과 거의 닮지 않았으며, 원래의 [Green 사양](https://apps.dtic.mil/sti/trecms/pdf/ADB950587.pdf) (경고: 4575페이지 분량의 거대한 스캔 PDF - 460페이지부터 시작)을 보면 전혀 닮지 않았음을 알 수 있습니다(해당 PDF의 505페이지). + +위의 소스 코드는 컴파일되지만, 강력한 정적 타입 시스템의 힘을 보여주는 경고도 발생합니다. + +## 이 소스 다운로드 + +GNAT 툴체인이 이미 설치되어 있다면, 위의 코드를 새 파일(예: ```learn-ada-in-y.ada```)에 복사하여 붙여넣은 다음 다음을 실행할 수 있습니다. + +```bash +$ gnatchop learn-ada-in-y.ada # 이것은 프로그램을 사양 ".ads"와 본문 ".adb"로 나눕니다. +$ gnatmake empty.adb # gnatmake는 모든 단위의 컴파일과 링크를 처리합니다. +$ gnatmake hello.adb +$ gnatmake learnadainy.adb +``` + +또는 [Alire](https://alire.ada.dev)를 다운로드하여 PATH의 어딘가에 복사한 다음 다음을 수행하십시오. + +**참고** Alire는 툴체인이 설치되어 있지 않은 경우 자동으로 설치하며 사용할 툴체인을 선택하도록 요청합니다. + +```bash +$ alr search learnadainy +$ alr get learnadainy +$ cd learnadainy +$ alr run empty +$ alr run hello +$ alr run learnadainy +``` + +## 더 읽을거리 + +* [Ada 프로그래밍 언어](https://ada-lang.io) +* [Ada 2022 참조 매뉴얼](https://ada-lang.io/docs/arm) +* [Ada 스타일 가이드](https://ada-lang.io/docs/style-guide/Ada_Style_Guide) +* [AdaCore 사이트에서 더 많은 Ada/Spark 배우기](https://learn.adacore.com) + +## 위 소스의 참고 자료 + +1. [위키북](https://en.wikibooks.org/wiki/Ada_Programming/Exceptions#Exception_handlers) +2. [C](https://ada-lang.io/docs/arm/AA-B/AA-B.3) +3. [Fortran](https://ada-lang.io/docs/arm/AA-B/AA-B.5/) +4. [COBOL](https://ada-lang.io/docs/arm/AA-B/AA-B.4/) +5. [동적 길이 문자열](https://ada-lang.io/docs/arm/AA-A/AA-A.4#Subclause_A.4.5) + +### 여러 줄 주석 + +여러 줄 주석은 오류가 발생하기 쉬우므로 허용되지 않습니다. + +> 이러한 주석은 닫는 주석 구분 기호가 필요하며, 이는 (의도하지 않은) 닫는 구분 기호 생략과 관련된 위험을 다시 제기합니다. 프로그램의 전체 섹션이 프로그래머가 깨닫지 못한 채 컴파일러에 의해 무시될 수 있습니다. +> +> [Ada 83 Rationale](http://archive.adaic.com/standards/83rat/html/ratl-02-01.html#2.1) diff --git a/ko/amd.md b/ko/amd.md new file mode 100644 index 0000000000..dc80a077f3 --- /dev/null +++ b/ko/amd.md @@ -0,0 +1,215 @@ +--- +category: tool +name: AMD +contributors: + - ["Frederik Ring", "https://github.com/m90"] +filename: learnamd.js +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +## AMD 시작하기 + +**비동기 모듈 정의(Asynchronous Module Definition)** API는 모듈과 그 의존성을 비동기적으로 로드할 수 있도록 자바스크립트 모듈을 정의하는 메커니즘을 지정합니다. 이는 모듈의 동기적 로딩이 성능, 사용성, 디버깅 및 교차 도메인 접근 문제를 야기하는 브라우저 환경에 특히 적합합니다. + +### 기본 개념 + +```javascript +// 기본 AMD API는 `define`과 `require`라는 두 가지 메서드로만 구성되며 +// 모듈 정의와 소비에 관한 것입니다: +// `define(id?, dependencies?, factory)`은 모듈을 정의합니다. +// `require(dependencies, callback)`은 의존성 집합을 가져와 +// 전달된 콜백에서 소비합니다. + +// 먼저 define을 사용하여 의존성이 없는 새로운 명명된 모듈을 정의해 보겠습니다. +// 이를 위해 define에 이름과 팩토리 함수를 전달합니다: +define('awesomeAMD', function(){ + var isAMDAwesome = function(){ + return true; + }; + // 모듈의 팩토리 함수 반환 값은 + // 다른 모듈이나 require 호출이 `awesomeAMD` 모듈을 + // 요구할 때 받게 되는 것입니다. + // 내보낸 값은 무엇이든 될 수 있습니다. (생성자) 함수, + // 객체, 원시 타입, 심지어 undefined도 가능합니다(별로 도움이 되지는 않지만). + return isAMDAwesome; +}); + +// 이제 `awesomeAMD` 모듈에 의존하는 다른 모듈을 정의해 보겠습니다. +// 이제 모듈의 의존성을 정의하는 추가 인수가 있음을 주목하십시오: +define('loudmouth', ['awesomeAMD'], function(awesomeAMD){ + // 의존성은 지정된 순서대로 팩토리의 인수에 + // 전달됩니다. + var tellEveryone = function(){ + if (awesomeAMD()){ + alert('This is sOoOo rad!'); + } else { + alert('Pretty dull, isn\'t it?'); + } + }; + return tellEveryone; +}); + +// 이제 define 사용법을 알았으니 `require`를 사용하여 +// 프로그램을 시작해 보겠습니다. `require`의 시그니처는 `(arrayOfDependencies, callback)`입니다. +require(['loudmouth'], function(loudmouth){ + loudmouth(); +}); + +// 이 튜토리얼에서 코드를 실행하기 위해, 바로 여기서 매우 기본적인 +// (비동기적이지 않은) AMD 버전을 구현해 보겠습니다: +function define(name, deps, factory){ + // 의존성이 없는 모듈이 어떻게 처리되는지 주목하십시오. + define[name] = require(factory ? deps : [], factory || deps); +} + +function require(deps, callback){ + var args = []; + // 먼저 require 호출에 필요한 모든 의존성을 + // 검색해 보겠습니다. + for (var i = 0; i < deps.length; i++){ + args[i] = define[deps[i]]; + } + // 콜백의 모든 의존성을 충족시킵니다. + return callback.apply(null, args); +} +// 이 코드가 작동하는 것을 여기에서 볼 수 있습니다: http://jsfiddle.net/qap949pd/ +``` + +### require.js를 사용한 실제 사용법 + +소개 예제와 달리 `require.js`(가장 인기 있는 AMD 라이브러리)는 실제로 **AMD**의 **A**를 구현하여 XHR을 통해 모듈과 그 의존성을 비동기적으로 로드할 수 있습니다: + +```javascript +/* file: app/main.js */ +require(['modules/someClass'], function(SomeClass){ + // 콜백은 의존성이 로드될 때까지 지연됩니다. + var thing = new SomeClass(); +}); +console.log('So here we are, waiting!'); // 이것이 먼저 실행됩니다. +``` + +관례적으로, 보통 하나의 모듈을 하나의 파일에 저장합니다. `require.js`는 파일 경로를 기반으로 모듈 이름을 확인할 수 있으므로 모듈에 이름을 지정할 필요 없이 위치를 사용하여 참조할 수 있습니다. 예제에서 `someClass`는 구성의 `baseUrl`에 상대적인 `modules` 폴더에 있다고 가정합니다: + +* app/ + * main.js + * modules/ + * someClass.js + * someHelpers.js + * ... + * daos/ + * things.js + * ... + +이는 모듈 ID를 지정하지 않고 `someClass`를 정의할 수 있음을 의미합니다: + +```javascript +/* file: app/modules/someClass.js */ +define(['daos/things', 'modules/someHelpers'], function(thingsDao, helpers){ + // 모듈 정의도 물론 비동기적으로 발생합니다. + function SomeClass(){ + this.method = function(){/**/}; + // ... + } + return SomeClass; +}); +``` + +기본 경로 매핑 동작을 변경하려면 `main.js`에서 `requirejs.config(configObj)`를 사용하십시오: + +```javascript +/* file: main.js */ +requirejs.config({ + baseUrl : 'app', + paths : { + // 다른 위치에서 모듈을 로드할 수도 있습니다. + jquery : '//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min', + coolLibFromBower : '../bower_components/cool-lib/coollib' + } +}); +require(['jquery', 'coolLibFromBower', 'modules/someHelpers'], function($, coolLib, helpers){ + // `main` 파일은 적어도 한 번 require를 호출해야 합니다. + // 그렇지 않으면 코드가 실행되지 않습니다. + coolLib.doFancyStuffWith(helpers.transform($('#foo'))); +}); +``` + +`require.js` 기반 앱은 일반적으로 `require.js` 스크립트 태그에 데이터 속성으로 전달되는 단일 진입점(`main.js`)을 가집니다. 페이지 로드 시 자동으로 로드되고 실행됩니다: + +```html + + + + A hundred script tags? Never again! + + + + + +``` + +### r.js를 사용하여 전체 프로젝트 최적화 + +많은 사람들이 개발 중에는 합리적인 코드 구성을 위해 AMD를 사용하지만, 프로덕션에서는 페이지 로드 시 수백 개의 XHR을 수행하는 대신 단일 스크립트 파일을 제공하기를 원합니다. + +`require.js`에는 `r.js`라는 스크립트가 함께 제공됩니다(node.js에서 실행할 가능성이 높지만 Rhino도 지원됨). 이 스크립트는 프로젝트의 의존성 그래프를 분석하고, 모든 모듈(적절하게 명명됨), 축소되고 소비 준비가 된 단일 파일을 빌드할 수 있습니다. + +`npm`을 사용하여 설치하십시오: + +```shell +$ npm install requirejs -g +``` + +이제 구성 파일을 제공할 수 있습니다: + +```shell +$ r.js -o app.build.js +``` + +위의 예제에 대한 구성은 다음과 같을 수 있습니다: + +```javascript +/* file : app.build.js */ +({ + name : 'main', // 진입점 이름 + out : 'main-built.js', // 출력을 쓸 파일 이름 + baseUrl : 'app', + paths : { + // `empty:`는 r.js에게 이것이 `main.js`에 지정된 위치를 사용하여 + // CDN에서 여전히 로드되어야 함을 알려줍니다. + jquery : 'empty:', + coolLibFromBower : '../bower_components/cool-lib/coollib' + } +}) +``` + +프로덕션에서 빌드된 파일을 사용하려면 `data-main`을 간단히 바꾸십시오: + +```html + +``` + +GitHub 리포지토리에서 빌드 옵션에 대한 매우 상세한 [개요](https://github.com/jrburke/r.js/blob/master/build/example.build.js)를 볼 수 있습니다. + +### 이 튜토리얼에서 다루지 않은 주제 +* [로더 플러그인 / 변환](http://requirejs.org/docs/plugins.html) +* [CommonJS 스타일 로딩 및 내보내기](http://requirejs.org/docs/commonjs.html) +* [고급 구성](http://requirejs.org/docs/api.html#config) +* [Shim 구성 (비 AMD 모듈 로딩)](http://requirejs.org/docs/api.html#config-shim) +* [require.js를 사용한 CSS 로딩 및 최적화](http://requirejs.org/docs/optimization.html#onecss) +* [빌드에 almond.js 사용](https://github.com/jrburke/almond) + +### 더 읽을거리: + +* [공식 사양](https://github.com/amdjs/amdjs-api/wiki/AMD) +* [왜 AMD인가?](http://requirejs.org/docs/whyamd.html) +* [범용 모듈 정의](https://github.com/umdjs/umd) + +### 구현: + +* [require.js](http://requirejs.org) +* [dojo toolkit](http://dojotoolkit.org/documentation/tutorials/1.9/modules/) +* [cujo.js](http://cujojs.com/) +* [curl.js](https://github.com/cujojs/curl) +* [lsjs](https://github.com/zazl/lsjs) +* [mmd](https://github.com/alexlawrence/mmd) diff --git a/ko/angularjs.md b/ko/angularjs.md new file mode 100644 index 0000000000..18ebe7ed55 --- /dev/null +++ b/ko/angularjs.md @@ -0,0 +1,706 @@ +--- +category: framework +name: AngularJS +contributors: + - ["Walter Cordero", "http://waltercordero.com"] +filename: learnangular.txt +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +## AngularJS 튜토리얼. + +AngularJS 버전 1.0은 2012년에 출시되었습니다. +Google 직원인 Miško Hevery는 2009년에 AngularJS 작업을 시작했습니다. +이 아이디어는 매우 성공적이었고, 현재 이 프로젝트는 Google에서 공식적으로 지원합니다. + +AngularJS는 자바스크립트 프레임워크입니다. "script" 태그를 사용하여 HTML 페이지에 추가할 수 있습니다. +AngularJS는 지시문(Directives)으로 HTML 속성을 확장하고, 표현식(Expressions)으로 데이터를 HTML에 바인딩합니다. + +## 이미 알고 있어야 할 사항 + +AngularJS를 공부하기 전에 다음에 대한 기본적인 이해가 있어야 합니다: + +- HTML +- CSS +- 자바스크립트 + +```html +// AngularJS는 자바스크립트 프레임워크입니다. 자바스크립트로 작성된 라이브러리입니다. +// AngularJS는 자바스크립트 파일로 배포되며, 스크립트 태그를 사용하여 웹 페이지에 추가할 수 있습니다: +// + +/////////////////////////////////// +// AngularJS는 HTML을 확장합니다 + +//AngularJS는 ng-지시문으로 HTML을 확장합니다. +//ng-app 지시문은 AngularJS 애플리케이션을 정의합니다. +//ng-model 지시문은 HTML 컨트롤(input, select, textarea)의 값을 애플리케이션 데이터에 바인딩합니다. +//ng-bind 지시문은 애플리케이션 데이터를 HTML 뷰에 바인딩합니다. + + + + +
+

이름:

+

+
+ + + +/* + * 예제 설명: + * 웹 페이지가 로드되면 AngularJS가 자동으로 시작됩니다. + * ng-app 지시문은
요소가 AngularJS 애플리케이션의 "소유자"임을 AngularJS에 알립니다. + * ng-model 지시문은 입력 필드의 값을 애플리케이션 변수 이름에 바인딩합니다. + * ng-bind 지시문은

요소의 innerHTML을 애플리케이션 변수 이름에 바인딩합니다. +*/ + 여기에 해석될 내용이 있습니다 + +/////////////////////////////////// +// AngularJS 표현식 + +// AngularJS 표현식은 이중 중괄호 안에 작성됩니다: {{ expression }}. +// AngularJS 표현식은 ng-bind 지시문과 동일한 방식으로 데이터를 HTML에 바인딩합니다. +// AngularJS는 표현식이 작성된 곳에 정확히 데이터를 "출력"합니다. +// AngularJS 표현식은 자바스크립트 표현식과 매우 유사합니다: 리터럴, 연산자 및 변수를 포함할 수 있습니다. +// 예제 {{ 5 + 5 }} 또는 {{ firstName + " " + lastName }} + + + + +

+

나의 첫 표현식: {{ 5 + 5 }}

+
+ + + +//ng-app 지시문을 제거하면 HTML은 표현식을 해결하지 않고 그대로 표시합니다: + + + + +
+

나의 첫 표현식: {{ 5 + 5 }}

+
+ + + +// AngularJS 표현식은 ng-bind 지시문과 동일한 방식으로 AngularJS 데이터를 HTML에 바인딩합니다. + + + + +
+

이름:

+

{{name}}

+
+ + + +// AngularJS 숫자는 자바스크립트 숫자와 같습니다: +
+

달러 총액: {{ quantity * cost }}

+
+ +//AngularJS 문자열은 자바스크립트 문자열과 같습니다: +
+

이름은

+
+ +//AngularJS 객체는 자바스크립트 객체와 같습니다: +
+

이름은 {{ person.lastName }}

+
+ +//AngularJS 배열은 자바스크립트 배열과 같습니다: +
+

세 번째 결과는 {{ points[2] }}

+
+ +// 자바스크립트 표현식과 마찬가지로 AngularJS 표현식은 리터럴, 연산자 및 변수를 포함할 수 있습니다. +// 자바스크립트 표현식과 달리 AngularJS 표현식은 HTML 내부에 작성할 수 있습니다. +// AngularJS 표현식은 조건문, 루프 및 예외를 지원하지 않지만 자바스크립트 표현식은 지원합니다. +// AngularJS 표현식은 필터를 지원하지만 자바스크립트 표현식은 지원하지 않습니다. + +/////////////////////////////////// +// AngularJS 지시문 + + +//AngularJS 지시문은 ng- 접두사가 붙은 확장된 HTML 속성입니다. +//ng-app 지시문은 AngularJS 애플리케이션을 초기화합니다. +//ng-init 지시문은 애플리케이션 데이터를 초기화합니다. +//ng-model 지시문은 HTML 컨트롤(input, select, textarea)의 값을 애플리케이션 데이터에 바인딩합니다. +
+

이름:

+

작성한 내용: {{ firstName }}

+
+ +//ng-init 사용은 그다지 일반적이지 않습니다. 컨트롤러에 대한 장에서 데이터를 초기화하는 방법을 배우게 됩니다. + +//ng-repeat 지시문은 HTML 요소를 반복합니다: +
+
    +
  • + {{ x }} +
  • +
+
+ +//객체 배열에 사용된 ng-repeat 지시문: +
+
    +
  • + {{ x.name + ', ' + x.country }} +
  • +
+
+ +// AngularJS는 데이터베이스 CRUD(생성 읽기 업데이트 삭제) 애플리케이션에 적합합니다. +// 이러한 객체가 데이터베이스의 레코드라고 상상해 보십시오. + +// ng-app 지시문은 AngularJS 애플리케이션의 루트 요소를 정의합니다. +// ng-app 지시문은 웹 페이지가 로드될 때 애플리케이션을 자동 부트스트랩(자동으로 초기화)합니다. +// 나중에 ng-app이 코드 모듈을 연결하기 위해 값(예: ng-app="myModule")을 가질 수 있는 방법을 배우게 됩니다. + +// ng-init 지시문은 AngularJS 애플리케이션의 초기 값을 정의합니다. +// 일반적으로 ng-init을 사용하지 않습니다. 대신 컨트롤러나 모듈을 사용합니다. +// 나중에 컨트롤러와 모듈에 대해 더 자세히 배우게 됩니다. + +//ng-model 지시문은 HTML 컨트롤(input, select, textarea)의 값을 애플리케이션 데이터에 바인딩합니다. +//ng-model 지시문은 다음을 수행할 수도 있습니다: +//애플리케이션 데이터에 대한 유형 유효성 검사 제공(숫자, 이메일, 필수). +//애플리케이션 데이터에 대한 상태 제공(유효하지 않음, 더티, 터치됨, 오류). +//HTML 요소에 대한 CSS 클래스 제공. +//HTML 요소를 HTML 양식에 바인딩. + +//ng-repeat 지시문은 컬렉션(배열)의 각 항목에 대해 한 번씩 HTML 요소를 복제합니다. + +/////////////////////////////////// +// AngularJS 컨트롤러 + +// AngularJS 컨트롤러는 AngularJS 애플리케이션의 데이터를 제어합니다. +// AngularJS 컨트롤러는 일반 자바스크립트 객체입니다. + +// AngularJS 애플리케이션은 컨트롤러에 의해 제어됩니다. +// ng-controller 지시문은 애플리케이션 컨트롤러를 정의합니다. +// 컨트롤러는 표준 자바스크립트 객체 생성자로 생성된 자바스크립트 객체입니다. + +
+ +이름:
+성:
+
+전체 이름: {{firstName + " " + lastName}} + +
+ + + +//애플리케이션 설명: + +//AngularJS 애플리케이션은 ng-app="myApp"으로 정의됩니다. 애플리케이션은
내에서 실행됩니다. +//ng-controller="myCtrl" 속성은 AngularJS 지시문입니다. 컨트롤러를 정의합니다. +//myCtrl 함수는 자바스크립트 함수입니다. +//AngularJS는 $scope 객체로 컨트롤러를 호출합니다. +//AngularJS에서 $scope는 애플리케이션 객체(애플리케이션 변수 및 함수의 소유자)입니다. +//컨트롤러는 스코프에 두 개의 속성(변수)(firstName 및 lastName)을 생성합니다. +//ng-model 지시문은 입력 필드를 컨트롤러 속성(firstName 및 lastName)에 바인딩합니다. + +//위의 예는 두 개의 속성(lastName 및 firstName)을 가진 컨트롤러 객체를 보여주었습니다. +//컨트롤러는 메서드(함수로서의 변수)를 가질 수도 있습니다: +
+ +이름:
+성:
+
+전체 이름: {{fullName()}} + +
+ + + +//더 큰 애플리케이션에서는 컨트롤러를 외부 파일에 저장하는 것이 일반적입니다. +// 태그 사이의 코드를 personController.js라는 외부 파일에 복사하기만 하면 됩니다: + +
+ +이름:
+성:
+
+전체 이름: {{firstName + " " + lastName}} + +
+ + + +// 다음 예제를 위해 새 컨트롤러 파일을 만들겠습니다: +angular.module('myApp', []).controller('namesCtrl', function($scope) { + $scope.names = [ + {name:'Jani',country:'Norway'}, + {name:'Hege',country:'Sweden'}, + {name:'Kai',country:'Denmark'} + ]; +}); + +//파일을 namesController.js로 저장합니다: +//그런 다음 애플리케이션에서 컨트롤러 파일을 사용합니다: + +
+ +
    +
  • + {{ x.name + ', ' + x.country }} +
  • +
+ +
+ + + +/////////////////////////////////// +// AngularJS 필터 + +// 필터는 파이프 문자를 사용하여 표현식 및 지시문에 추가할 수 있습니다. +// AngularJS 필터는 데이터를 변환하는 데 사용할 수 있습니다: + +- **currency**: 숫자를 통화 형식으로 지정합니다. +- **filter**: 배열에서 항목의 하위 집합을 선택합니다. +- **lowercase**: 문자열을 소문자로 지정합니다. +- **orderBy**: 표현식으로 배열을 정렬합니다. +- **uppercase**: 문자열을 대문자로 지정합니다. + +//필터는 파이프 문자(|)와 필터를 사용하여 표현식에 추가할 수 있습니다. +//(다음 두 예제에서는 이전 장의 person 컨트롤러를 사용합니다) +//대문자 필터는 문자열을 대문자로 지정합니다: +
+ +

이름은 {{ lastName | uppercase }}

+ +
+ +//소문자 필터는 문자열을 소문자로 지정합니다: +
+ +

이름은 {{ lastName | lowercase }}

+ +
+ +//통화 필터는 숫자를 통화로 지정합니다: +
+ + + + +

총액 = {{ (quantity * price) | currency }}

+ +
+ +//필터는 파이프 문자(|)와 필터를 사용하여 지시문에 추가할 수 있습니다. +//orderBy 필터는 표현식으로 배열을 정렬합니다: +
+ +
    +
  • + {{ x.name + ', ' + x.country }} +
  • +
+ +
+ +//입력 필터는 파이프 문자(|)와 필터, 콜론 및 모델 이름을 사용하여 지시문에 추가할 수 있습니다. +//필터는 배열의 하위 집합을 선택합니다: + +
+ +

+ +
    +
  • + {{ (x.name | uppercase) + ', ' + x.country }} +
  • +
+ +
+ +/////////////////////////////////// +// AngularJS AJAX - $http + +//$http는 원격 서버에서 데이터를 읽기 위한 AngularJS 서비스입니다. + +// 다음 데이터는 웹 서버에서 제공할 수 있습니다: +// http://www.w3schools.com/angular/customers.php +// **데이터 형식을 보려면 URL을 확인하십시오** + +// AngularJS $http는 웹 서버에서 데이터를 읽기 위한 핵심 서비스입니다. +// $http.get(url)은 서버 데이터를 읽는 데 사용할 함수입니다. +
+ +
    +
  • + {{ x.Name + ', ' + x.Country }} +
  • +
+ +
+ + + +애플리케이션 설명: + +// AngularJS 애플리케이션은 ng-app으로 정의됩니다. 애플리케이션은
내에서 실행됩니다. +// ng-controller 지시문은 컨트롤러 객체의 이름을 지정합니다. +// customersCtrl 함수는 표준 자바스크립트 객체 생성자입니다. +// AngularJS는 $scope 및 $http 객체로 customersCtrl을 호출합니다. +// $scope는 애플리케이션 객체(애플리케이션 변수 및 함수의 소유자)입니다. +// $http는 외부 데이터를 요청하기 위한 XMLHttpRequest 객체입니다. +// $http.get()은 http://www.w3schools.com/angular/customers.php에서 JSON 데이터를 읽습니다. +// 성공하면 컨트롤러는 서버의 JSON 데이터로 스코프에 속성(이름)을 생성합니다. + + +// 다른 서버(요청 페이지와 다른)의 데이터 요청은 교차 사이트 HTTP 요청이라고 합니다. +// 교차 사이트 요청은 웹에서 일반적입니다. 많은 페이지가 다른 서버에서 CSS, 이미지 및 스크립트를 로드합니다. +// 최신 브라우저에서는 스크립트의 교차 사이트 HTTP 요청이 보안상의 이유로 동일한 사이트로 제한됩니다. +// PHP 예제의 다음 줄은 교차 사이트 액세스를 허용하기 위해 추가되었습니다. +header("Access-Control-Allow-Origin: *"); + + +/////////////////////////////////// +// AngularJS 테이블 + +// angular로 테이블을 표시하는 것은 매우 간단합니다: +
+ + + + + + +
{{ x.Name }}{{ x.Country }}
+ +
+ + + +// 테이블을 정렬하려면 orderBy 필터를 추가하십시오: + + + + + +
{{ x.Name }}{{ x.Country }}
+ +// 테이블 인덱스를 표시하려면 $index가 있는 를 추가하십시오: + + + + + + +
{{ $index + 1 }}{{ x.Name }}{{ x.Country }}
+ +// $even 및 $odd 사용 + + + + + + + +
{{ x.Name }}{{ x.Name }}{{ x.Country }}{{ x.Country }}
+ +/////////////////////////////////// +// AngularJS HTML DOM + +//AngularJS에는 애플리케이션 데이터를 HTML DOM 요소의 속성에 바인딩하는 지시문이 있습니다. + +// ng-disabled 지시문은 AngularJS 애플리케이션 데이터를 HTML 요소의 disabled 속성에 바인딩합니다. + +
+ +

+ +

+ +

+버튼 +

+ +
+ +//애플리케이션 설명: + +// ng-disabled 지시문은 애플리케이션 데이터 mySwitch를 HTML 버튼의 disabled 속성에 바인딩합니다. +// ng-model 지시문은 HTML 체크박스 요소의 값을 mySwitch의 값에 바인딩합니다. +// mySwitch의 값이 true로 평가되면 버튼이 비활성화됩니다: +

+ +

+ +// mySwitch의 값이 false로 평가되면 버튼이 비활성화되지 않습니다: +

+ +

+ +// ng-show 지시문은 HTML 요소를 표시하거나 숨깁니다. + +
+ +

나는 보입니다.

+ +

나는 보이지 않습니다.

+ +
+ +// ng-show 지시문은 ng-show의 값에 따라 HTML 요소를 표시(또는 숨김)합니다. +// true 또는 false로 평가되는 모든 표현식을 사용할 수 있습니다: +
+

나는 보입니다.

+
+ +/////////////////////////////////// +// AngularJS 이벤트 + +// AngularJS에는 자체 HTML 이벤트 지시문이 있습니다. + +// ng-click 지시문은 AngularJS 클릭 이벤트를 정의합니다. +
+ + + +

{{ count }}

+ +
+ + +// ng-hide 지시문은 애플리케이션 일부의 가시성을 설정하는 데 사용할 수 있습니다. +// ng-hide="true" 값은 HTML 요소를 보이지 않게 만듭니다. +// ng-hide="false" 값은 요소를 보이게 만듭니다. +
+ + + +

+이름:
+성:
+
+전체 이름: {{firstName + " " + lastName}} +

+ +
+ + + +//애플리케이션 설명: + +// personController의 첫 번째 부분은 컨트롤러에 대한 장과 동일합니다. +// 애플리케이션에는 기본 속성(변수)이 있습니다: $scope.myVar = false; +// ng-hide 지시문은 myVar의 값(true 또는 false)에 따라 두 개의 입력 필드가 있는

요소의 가시성을 설정합니다. +// toggle() 함수는 myVar를 true와 false 사이에서 토글합니다. +// ng-hide="true" 값은 요소를 보이지 않게 만듭니다. + + +// ng-show 지시문은 애플리케이션 일부의 가시성을 설정하는 데에도 사용할 수 있습니다. +// ng-show="false" 값은 HTML 요소를 보이지 않게 만듭니다. +// ng-show="true" 값은 요소를 보이게 만듭니다. +// 다음은 ng-hide 대신 ng-show를 사용하는 위와 동일한 예입니다: +

+ + + +

+이름:
+성:
+
+전체 이름: {{firstName + " " + lastName}} +

+ +
+ + + +/////////////////////////////////// +// AngularJS 모듈 + +// AngularJS 모듈은 애플리케이션을 정의합니다. +// 모듈은 애플리케이션의 다른 부분을 위한 컨테이너입니다. +// 모듈은 애플리케이션 컨트롤러를 위한 컨테이너입니다. +// 컨트롤러는 항상 모듈에 속합니다. + +// 이 애플리케이션("myApp")에는 하나의 컨트롤러("myCtrl")가 있습니다: + + + + + + +
+{{ firstName + " " + lastName }} +
+ + + + + + +// AngularJS 애플리케이션에서는 모듈과 컨트롤러를 자바스크립트 파일에 넣는 것이 일반적입니다. +// 이 예제에서 "myApp.js"는 애플리케이션 모듈 정의를 포함하고 "myCtrl.js"는 컨트롤러를 포함합니다: + + + + + + +
+{{ firstName + " " + lastName }} +
+ + + + + + + +//myApp.js +var app = angular.module("myApp", []); + +// 모듈 정의의 [] 매개변수는 종속 모듈을 정의하는 데 사용할 수 있습니다. + +// myCtrl.js +app.controller("myCtrl", function($scope) { + $scope.firstName = "John"; + $scope.lastName= "Doe"; +}); + +// 전역 함수는 자바스크립트에서 피해야 합니다. 다른 스크립트에 의해 쉽게 덮어쓰이거나 파괴될 수 있습니다. + +// AngularJS 모듈은 모든 함수를 모듈에 로컬로 유지하여 이 문제를 줄입니다. + +// HTML 애플리케이션에서는 스크립트를 요소의 끝에 배치하는 것이 일반적이지만 +// AngularJS 라이브러리를 또는 의 시작 부분에 로드하는 것이 좋습니다. + +// 이는 angular.module에 대한 호출이 라이브러리가 로드된 후에만 컴파일될 수 있기 때문입니다. + + + + + + +
+{{ firstName + " " + lastName }} +
+ + + + + + + +/////////////////////////////////// +// AngularJS 애플리케이션 + +// AngularJS 모듈은 AngularJS 애플리케이션을 정의합니다. +// AngularJS 컨트롤러는 AngularJS 애플리케이션을 제어합니다. +// ng-app 지시문은 애플리케이션을 정의하고, ng-controller 지시문은 컨트롤러를 정의합니다. +
+ 이름:
+ 성:
+
+ 전체 이름: {{firstName + " " + lastName}} +
+ + +// AngularJS 모듈은 애플리케이션을 정의합니다: +var app = angular.module('myApp', []); + +// AngularJS 컨트롤러는 애플리케이션을 제어합니다: +app.controller('myCtrl', function($scope) { + $scope.firstName= "John"; + $scope.lastName= "Doe"; +}); +``` + +## 소스 및 참고 자료 + +**예제** + +- [http://www.w3schools.com/angular/angular_examples.asp](http://www.w3schools.com/angular/angular_examples.asp) + +**참고 자료** + +- [http://www.w3schools.com/angular/angular_ref_directives.asp](http://www.w3schools.com/angular/angular_ref_directives.asp) +- [http://www.w3schools.com/angular/default.asp](http://www.w3schools.com/angular/default.asp) +- [https://teamtreehouse.com/library/angular-basics/](https://teamtreehouse.com/library/angular-basics/) diff --git a/ko/ansible.md b/ko/ansible.md new file mode 100644 index 0000000000..07faa3a1a8 --- /dev/null +++ b/ko/ansible.md @@ -0,0 +1,682 @@ +--- +category: tool +name: Ansible +contributors: + - ["Jakub Muszynski" , "http://github.com/sirkubax"] + - ["Pat Myron" , "https://github.com/patmyron"] + - ["Divay Prakash", "https://github.com/divayprakash"] +filename: LearnAnsible.txt +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +## 소개 + +```yaml +--- +"{{ Ansible }}"은 Python으로 작성된 오케스트레이션 도구입니다. +... +``` + +Ansible은 (많은 것 중 하나인) 오케스트레이션 도구입니다. +환경(인프라 및 코드)을 제어하고 수동 작업을 자동화할 수 있습니다. + +Ansible은 여러 운영 체제(Windows 포함) 및 일부 하드웨어(스위치, 방화벽 등)와 훌륭하게 통합됩니다. 클라우드 제공업체와 통합되는 여러 도구가 있습니다. 거의 모든 주목할 만한 클라우드 제공업체가 생태계에 존재합니다(AWS, Azure, Google, DigitalOcean, OVH 등). + +하지만 ansible은 그 이상입니다! 실행 계획, API, 라이브러리 및 콜백을 제공합니다. + +### 주요 장단점 + +#### 장점 + +* 에이전트 없는 도구입니다. 대부분의 시나리오에서 ssh를 전송 계층으로 사용합니다. +어떤 면에서는 '강화된 bash'로 사용할 수 있습니다. +* 시작하기가 매우 쉽습니다. ssh 개념에 익숙하다면 이미 Ansible을 알고 있는 것입니다(거의). +* '있는 그대로' 실행됩니다 - 다른 도구(salt, puppet, chef - 예상과 다른 시나리오에서 실행될 수 있음) +* 문서는 세계 최고 수준입니다! +* 자신만의 모듈과 확장을 작성하는 것은 상당히 쉽습니다. +* Ansible AWX는 우리가 기다려온 Ansible Tower의 오픈 소스 버전으로, 훌륭한 UI를 제공합니다. + +#### 단점 + +* 에이전트 없는 도구입니다 - 모든 에이전트는 최대 16MB의 RAM을 소비합니다 - 일부 환경에서는 눈에 띄는 양일 수 있습니다. +* 에이전트가 없습니다 - 환경 일관성을 '온디맨드'로 확인해야 합니다 - 일부 변경 사항에 대해 자동으로 경고하는 내장 메커니즘이 없습니다(합리적인 노력으로 달성 가능). +* 공식 GUI - Ansible Tower -는 훌륭하지만 비쌉니다. +* '소규모 기업' 지불 계획은 없지만, Ansible AWX는 우리 모두가 기다려온 무료 오픈 소스 버전입니다. + +#### 중립 + +마이그레이션 - Ansible <-> Salt는 상당히 쉽습니다 - 따라서 이벤트 기반 에이전트 환경이 필요한 경우 Ansible로 빠르게 시작하고 필요할 때 Salt로 변환하는 것이 좋은 선택이 될 것입니다. + +#### 일부 개념 + +Ansible은 ssh 또는 paramiko를 전송 계층으로 사용합니다. 어떤 면에서는 작업을 수행하기 위해 API와 함께 ssh를 사용한다고 상상할 수 있습니다. 가장 간단한 방법은 더 제어된 방식으로 원격 명령을 실행하는 것입니다(여전히 ssh 사용). +반면에 고급 범위에서는 자신의 Python 스크립트로 Ansible을 래핑(Python Ansible 코드를 라이브러리로 사용)할 수 있습니다! 그러면 Fabric처럼 작동합니다. + +## 예제 + +apache를 설치하고 로그 수준을 구성하는 예제 플레이북 + +```yaml +--- +- hosts: apache + + vars: + apache2_log_level: "warn" + + handlers: + - name: restart apache + service: + name: apache2 + state: restarted + enabled: True + notify: + - Wait for instances to listen on port 80 + become: True + + - name: reload apache + service: + name: apache2 + state: reloaded + notify: + - Wait for instances to listen on port 80 + become: True + + - name: Wait for instances to listen on port 80 + wait_for: + state: started + host: localhost + port: 80 + timeout: 15 + delay: 5 + + tasks: + - name: Update cache + apt: + update_cache: yes + cache_valid_time: 7200 + become: True + + - name: Install packages + apt: + name={{ item }} + with_items: + - apache2 + - logrotate + notify: + - restart apache + become: True + + - name: Configure apache2 log level + lineinfile: + dest: /etc/apache2/apache2.conf + line: "LogLevel {{ apache2_log_level }}" + regexp: "^LogLevel" + notify: + - reload apache + become: True +... +``` + +## 설치 + +```bash +# 범용 방법 +$ pip install ansible + +# Debian, Ubuntu +$ apt-get install ansible +``` + +* [부록 A - ansible을 어떻게 설치하나요?](#infrastructure-as-a-code) +* [추가 자료.](http://docs.ansible.com/ansible/latest/intro_installation.html) + +### 첫 번째 ansible 명령 (셸 실행) + +```bash +# 명령이 localhost를 ping합니다 (기본 인벤토리: /etc/ansible/hosts에 정의됨) +$ ansible -m ping localhost +# 이 출력이 표시되어야 합니다 +localhost | SUCCESS => { + "changed": false, + "ping": "pong" +} +``` + +### 셸 명령 + +알아야 할 몇 가지 명령이 있습니다 + +* `ansible` (CLI에서 모듈 실행) +* `ansible-playbook` (플레이북 실행) +* `ansible-vault` (비밀 관리) +* `ansible-galaxy` (github/galaxy에서 역할 설치) + +### 모듈 + +실행되고, 일부 작업을 수행하고, 적절한 JSON 출력을 반환하는 프로그램(보통 python)입니다. 이 프로그램은 전문화된 작업/액션(클라우드에서 인스턴스 관리, 셸 명령 실행 등)을 수행합니다. 가장 간단한 모듈은 `ping`이라고 하며, `pong` 메시지가 있는 JSON을 반환합니다. + +모듈 예제: + +* 모듈: `ping` - 호스트 연결을 확인하는 데 유용한 가장 간단한 모듈 +* 모듈: `shell` - 지정된 호스트에서 셸 명령을 실행하는 모듈. + + +```bash +$ ansible -m ping all +$ ansible -m shell -a 'date; whoami' localhost #hostname_or_a_group_name +``` + +* 모듈: `command` - 셸을 통해 처리되지 않는 단일 명령을 실행하므로 `$HOME`과 같은 변수나 ``|` `;``와 같은 피연산자는 작동하지 않습니다. command 모듈은 사용자의 환경에 영향을 받지 않으므로 더 안전합니다. 더 복잡한 명령의 경우 - 셸 모듈을 사용하십시오. + +```bash +$ ansible -m command -a 'date; whoami' # 실패 +$ ansible -m command -a 'date' all +$ ansible -m command -a 'whoami' all +``` + +* 모듈: `file` - 파일 작업 수행 (stat, link, dir, ...) +* 모듈: `raw` - 모듈 하위 시스템을 거치지 않고 저수준의 더러운 SSH 명령을 실행합니다 (python2.7 설치에 유용). + +### 작업 + +단일 Ansible **모듈**의 실행을 **작업**이라고 합니다. 위에서 볼 수 있듯이 가장 간단한 모듈은 `ping`이라고 합니다. + +여러 리소스에서 원격으로 명령을 실행할 수 있는 또 다른 모듈 예제는 `shell`입니다. 위에서 이미 사용한 방법을 참조하십시오. + +### 플레이북 + +스크립트 파일 형식으로 작성된 **실행 계획**을 **플레이북**이라고 합니다. +플레이북은 여러 요소로 구성됩니다 - +* '플레이'가 실행되는 호스트 목록(또는 그룹) +* 실행될 `작업` 또는 `역할` +* 여러 선택적 설정 (기본 변수 등) + +플레이북 스크립트 언어는 YAML입니다. 플레이북은 실행하는 매우 고급 CLI 스크립트라고 생각할 수 있습니다. + +#### 플레이북 예제 + +이 예제 플레이북은 (인벤토리에 정의된 모든 호스트에서) 두 가지 작업을 실행합니다: +* 메시지 *pong*을 반환하는 `ping` +* 세 가지 명령을 실행하고 출력을 터미널로 반환하는 `shell` + +```yaml +- hosts: all + + tasks: + - name: "ping all" + ping: + + - name: "execute a shell command" + shell: "date; whoami; df -h;" +``` + +다음 명령으로 플레이북을 실행합니다: + +```bash +$ ansible-playbook path/name_of_the_playbook.yml +``` + +참고: 예제 플레이북은 다음 장인 '역할'에서 설명합니다. + +### ansible 개념에 대한 추가 정보 + +### 인벤토리 + +인벤토리는 플레이북 또는 셸 명령을 통해 단일 작업을 실행하는 대상 개체 또는 호스트 집합입니다. 이 몇 분 동안은 기본 ansible 인벤토리(Debian 기반 시스템에서는 `/etc/ansible/hosts`에 있음)를 사용한다고 가정해 보겠습니다. + +``` +localhost + +[some_group] +hostA.mydomain.com +hostB.localdomain +1.2.3.4 + +[a_group_of_a_groups:children] +some_group +some_other_group +``` + +* [추가 자료.](http://docs.ansible.com/ansible/latest/intro_inventory.html) + +### ansible-roles ('올바른 구조를 가진 템플릿 플레이북') + +이미 CLI를 통해 작업(모듈)을 실행할 수 있다는 것을 알고 있습니다. 또한 여러 작업(변수 및 논리가 있는)의 실행 계획인 플레이북도 알고 있습니다. + +재사용해야 하는 코드 부분(플레이북)에 대해 `역할`이라는 개념이 도입되었습니다. + +**역할**은 작업, 변수, 핸들러, 기본 설정 등을 관리하는 구조화된 방법입니다(메타, 파일, 템플릿). 역할은 여러 플레이북에서 동일한 코드 부분을 재사용할 수 있도록 합니다(실행 중에 역할을 '추가로' 매개변수화할 수 있음). 애플리케이션에 대한 `객체 지향` 관리를 도입하는 좋은 방법입니다. + +역할은 플레이북에 포함될 수 있습니다(플레이북을 통해 실행됨). + + +```yaml +- hosts: all + + tasks: + - name: "ping all" + ping: + - name: "execute a shell command" + shell: "date; whoami; df -h;" + + roles: + - some_role + - { role: another_role, some_variable: 'learnxiny', tags: ['my_tag'] } + + pre_tasks: + - name: some pre-task + shell: echo 'this task is the last, but would be executed before roles, and before tasks' +``` + +#### 나머지 예제에서는 추가 리포지토리를 사용합니다 +이 예제는 `virtualenv`에 ansible을 설치하므로 시스템과 독립적입니다. +`source environment.sh` 명령으로 셸 컨텍스트에 초기화해야 합니다. + +다음 예제 리포지토리를 사용합니다: [https://github.com/sirkubax/ansible-for-learnXinYminutes](https://github.com/sirkubax/ansible-for-learnXinYminutes) + +```bash +$ # 다음 예제에는 venv 및 상대 경로를 나타내는 셸 프롬프트가 포함되어 있습니다. +$ git clone git@github.com:sirkubax/ansible-for-learnXinYminutes.git +user@host:~/$ cd ansible-for-learnXinYminutes +user@host:~/ansible-for-learnXinYminutes$ source environment.sh +$ +$ # 먼저 simple_playbook.yml을 실행해 보겠습니다. +(venv) user@host:~/ansible-for-learnXinYminutes$ ansible-playbook playbooks/simple_playbook.yml +``` + +역할 예제로 플레이북 실행 + +```bash +$ source environment.sh +$ # 이제 역할이 있는 위 플레이북을 실행합니다. +(venv) user@host:~/ansible-for-learnXinYminutes$ ansible-playbook playbooks/simple_role.yml +``` + +#### 역할 디렉토리 구조 + +``` +roles/ + some_role/ + defaults/ # 기본 변수 포함 + files/ # 정적 파일용 + templates/ # jinja 템플릿용 + tasks/ # 작업 + handlers/ # 핸들러 + vars/ # 더 많은 변수 (우선 순위 높음) + meta/ # 메타 - 패키지 (역할) 정보 +``` + +#### 역할 핸들러 +핸들러는 플레이북 실행 중에 트리거(알림)될 수 있는 작업이지만, 플레이북의 맨 끝에서 실행됩니다. 서비스를 다시 시작하거나, 애플리케이션 포트가 활성 상태인지 확인(성공적인 배포 기준)하는 등의 가장 좋은 방법입니다. + +simple_apache_role 예제에서 역할을 사용하는 방법을 숙지하십시오. + +``` +playbooks/roles/simple_apache_role/ +├── tasks +│ └── main.yml +└── templates + └── main.yml +``` + +### ansible - 변수 + +Ansible은 유연합니다 - 21단계의 변수 우선 순위가 있습니다. +[더 읽기](http://docs.ansible.com/ansible/latest/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable) +지금은 CLI 변수가 최우선 순위를 갖는다는 것을 알아야 합니다. +또한 일부 데이터를 풀링하는 좋은 방법은 **조회**라는 것을 알아야 합니다. + +### 조회 +다양한 소스에서 데이터를 쿼리하는 멋진 도구!!! 멋지다! +쿼리 대상: +* 파이프 (셸 명령 출력을 변수로 로드!) +* 파일 +* 스트림 +* etcd +* 암호 관리 도구 +* url + +```bash +# playbooks/lookup.yml 읽기 +# 그런 다음 실행 +(venv) user@host:~/ansible-for-learnXinYminutes$ ansible-playbook playbooks/lookup.yml +``` + +CLI에서도 사용할 수 있습니다. + +```yaml +ansible -m shell -a 'echo "{{ my_variable }}"' -e 'my_variable="{{ lookup("pipe", "date") }}"' localhost +ansible -m shell -a 'echo "{{ my_variable }}"' -e 'my_variable="{{ lookup("pipe", "hostname") }}"' all + +# 또는 플레이북에서 사용 + +(venv) user@host:~/ansible-for-learnXinYminutes$ ansible-playbook playbooks/lookup.yml +``` + +### 등록 및 조건부 + +#### 등록 + +변수 내용을 동적으로 생성하는 또 다른 방법은 `register` 명령입니다. +`Register`는 작업의 출력을 저장하고 해당 값을 사용하여 추가 작업을 실행하는 데에도 유용합니다. + +``` +(venv) user@host:~/ansible-for-learnXinYminutes$ ansible-playbook playbooks/register_and_when.yml +``` + +```yaml +--- +- hosts: localhost + tasks: + - name: check the system capacity + shell: df -h / + register: root_size + + - name: debug root_size + debug: + msg: "{{ root_size }}" + + - name: debug root_size return code + debug: + msg: "{{ root_size.rc }}" + +# when: 예제 + + - name: Print this message when return code of 'check the system capacity' was ok + debug: + msg: "{{ root_size.rc }}" + when: root_size.rc == 0 +... +``` + +#### 조건부 - when: + +Ansible 및 Jinja 함수를 사용하여 복잡한 논리를 정의할 수 있습니다. 가장 일반적인 것은 `when:`을 일부 변수(종종 이전 플레이북 단계에서 `register` 또는 `lookup`으로 동적으로 생성됨)와 함께 사용하는 것입니다. + +```yaml +--- +- hosts: localhost + tasks: + - name: check the system capacity + shell: df -h / + when: some_variable in 'a string' + roles: + - { role: mid_nagios_probe, when: allow_nagios_probes } +... +``` + +### ansible - 태그, 제한 + +이 간단한 기능으로 효율성을 높이는 방법에 대해 알아야 합니다. + +#### 태그 + +작업, 역할(및 해당 작업), 포함 등을 태그 지정한 다음 태그가 지정된 리소스만 실행할 수 있습니다. + +``` +ansible-playbook playbooks/simple_playbook.yml --tags=tagA,tag_other +ansible-playbook playbooks/simple_playbook.yml -t tagA,tag_other + +특수 태그가 있습니다: + always + +--skip-tags를 사용하여 코드 블록을 제외할 수 있습니다. +--list-tags를 사용하여 사용 가능한 태그를 나열합니다. +``` + +[더 읽기](http://docs.ansible.com/ansible/latest/playbooks_tags.html) + +#### 제한 + +작업 실행을 정의된 호스트로 제한할 수 있습니다. + +``` +ansible-playbook playbooks/simple_playbook.yml --limit localhost + +--limit my_hostname +--limit groupname +--limit some_prefix* +--limit hostname:group #JM +``` + +### 템플릿 + +템플릿은 일부 (부분적으로) 동적 콘텐츠를 제공하는 강력한 방법입니다. +Ansible은 **Jinja2** 언어를 사용하여 템플릿을 설명합니다. + +``` +일부 정적 콘텐츠 + +{{ a_variable }} + +{% for item in loop_items %} + 이 줄 항목은 {{ item }}입니다. +{% endfor %} +``` + +Jinja에는 몇 가지 제한 사항이 있을 수 있지만, 좋아할 만한 강력한 도구입니다. + +apache2를 설치하고 템플릿에서 index.html을 생성하는 이 간단한 예제를 검토하십시오. +"playbooks/roles/simple_apache_role/templates/index.html" + +```bash +$ source environment.sh +$ # 이제 역할이 있는 위 플레이북을 실행합니다. +(venv) user@host:~/ansible-for-learnXinYminutes$ ansible-playbook playbooks/simple_role.yml --tags apache2 +``` + +#### Jinja2 CLI + +CLI에서도 jinja를 사용할 수 있습니다. + +```bash +ansible -m shell -a 'echo {{ my_variable }}' -e 'my_variable=something, playbook_parameter=twentytwo' localhost +``` + +사실 - jinja는 플레이북의 일부를 템플릿화하는 데에도 사용됩니다. + +```yaml +# 이 플레이북의 일부 확인: playbooks/roles/sys_debug/tasks/debug_time.yml +- local_action: shell date +'%F %T' + register: ts + become: False + changed_when: False + +- name: Timestamp + debug: msg="{{ ts.stdout }}" + when: ts is defined and ts.stdout is defined + become: False +``` + +#### Jinja2 필터 + +Jinja는 강력합니다. 유용한 내장 함수가 많이 있습니다. + +``` +# 목록의 첫 번째 항목 가져오기 +{{ some_list | first() }} +# 변수가 정의되지 않은 경우 - 기본값 사용 +{{ some_variable | default('default_value') }} +``` + +[더 읽기](http://docs.ansible.com/ansible/latest/playbooks_filters.html) + +### ansible-vault + +**코드로 인프라**를 유지하려면 비밀을 저장해야 합니다. Ansible은 기밀 파일을 암호화하는 방법을 제공하므로 리포지토리에 저장할 수 있지만, 파일은 ansible 실행 중에 즉시 해독됩니다. + +가장 좋은 방법은 비밀을 안전한 위치에 저장하고 런타임에 사용하도록 ansible을 구성하는 것입니다. + +```bash +# 시도 (실패함) +$ ansible-playbook playbooks/vault_example.yml + +$ echo some_very_very_long_secret > ~/.ssh/secure_located_file + +# ansible.cfg에서 비밀 파일 경로 설정 +$ vi ansible.cfg + ansible_vault_password_file = ~/.ssh/secure_located_file + +# 또는 env 사용 +$ export ANSIBLE_VAULT_PASSWORD_FILE=~/.ssh/secure_located_file + +$ ansible-playbook playbooks/vault_example.yml + + # 파일 암호화 +$ ansible-vault encrypt path/somefile + + # 파일 보기 +$ ansible-vault view path/somefile + + # 파일 내용 확인: +$ cat path/somefile + + # 파일 해독 +$ ansible-vault decrypt path/somefile +``` + +### 동적 인벤토리 + +인벤토리를 동적으로 빌드할 수 있다는 것을 좋아할 것입니다. +(Ansible의 경우) 인벤토리는 적절한 구조를 가진 JSON일 뿐입니다 - ansible에 전달할 수 있다면 무엇이든 가능합니다. + +바퀴를 다시 발명할 필요가 없습니다 - 가장 인기 있는 클라우드 제공업체 및 많은 사내 인기 사용 사례에 대한 즉시 사용 가능한 인벤토리 스크립트가 많이 있습니다. + +[AWS 예제](http://docs.ansible.com/ansible/latest/intro_dynamic_inventory.html#example-aws-ec2-external-inventory-script) + +```bash +$ etc/inv/ec2.py --refresh +$ ansible -m ping all -i etc/inv/ec2.py +``` + +[더 읽기](http://docs.ansible.com/ansible/latest/intro_dynamic_inventory.html) + +### ansible 프로파일링 - 콜백 + +플레이북 실행에는 시간이 걸립니다. 괜찮습니다. 먼저 실행되도록 한 다음 속도를 높이고 싶을 수 있습니다. ansible 2.x부터 작업 실행 프로파일링을 위한 내장 콜백이 있습니다. + +``` +vi ansible.cfg +# 이것을 다음으로 설정: +callback_whitelist = profile_tasks +``` + +### facts-cache 및 ansible-cmdb + +다른 호스트에서 환경에 대한 일부 정보를 가져올 수 있습니다. 정보가 변경되지 않으면 facts_cache를 사용하여 속도를 높이는 것을 고려할 수 있습니다. + +``` +vi ansible.cfg + +# 영구 유형('memory'가 아닌, 예를 들어 'redis')으로 설정하면 이전 Ansible 실행의 팩트 값이 저장됩니다. 예를 들어, 현재 IP 정보를 얻기 위해 동일한 플레이북 실행에서 통신할 필요 없이 한 그룹의 서버에서 IP 정보를 사용하려는 경우 유용할 수 있습니다. +fact_caching = jsonfile +fact_caching_connection = ~/facts_cache +fact_caching_timeout = 86400 +``` + +백엔드로 `jsonfile`을 사용하는 것을 좋아합니다. 인벤토리 리소스의 HTML 페이지를 생성하는 다른 프로젝트 `ansible-cmdb` [(GitHub 프로젝트)](https://github.com/fboender/ansible-cmdb)를 사용할 수 있습니다. 좋은 '무료' 추가 기능입니다! + +### ansible 디버깅 [진행 중인 장] + +작업이 실패하면 디버깅에 효과적이어야 합니다. + +1. 여러 -v **[ -vvvvv]**를 사용하여 상세도를 높입니다. +2. 변수가 정의되지 않은 경우 - +`grep -R path_of_your_inventory -e missing_variable` +3. 변수(사전 또는 목록)가 정의되지 않은 경우 - +`grep -R path_of_your_inventory -e missing_variable` +4. Jinja 템플릿 디버그 +5. 이상한 동작 - '대상에서' 코드를 실행해 보십시오. + +### 코드로 인프라 + +ansible-vault를 사용하면 기밀 데이터를 코드와 함께 저장할 수 있다는 것을 이미 알고 있습니다. 더 나아가 ansible 설치 및 구성을 코드로 정의할 수 있습니다. `environment.sh`를 참조하여 운영 체제에 연결되지 않은 `virtualenv` 내에 ansible 자체를 설치하는 방법을 알아보십시오(권한 없는 사용자가 변경할 수 있음). 추가적인 이점으로 ansible 버전 업그레이드는 새 virtualenv에 새 버전을 설치하는 것만큼 쉽습니다. 또한 여러 버전의 Ansible을 동시에 사용할 수 있습니다. + +```bash +# ansible 2.x venv 다시 만들기 +$ rm -rf venv2 +$ source environment2.sh + +# 플레이북 실행 +(venv2)$ ansible-playbook playbooks/ansible1.9_playbook.yml # 실패함 - 더 이상 사용되지 않는 구문 + +# 이제 ansible 2.x 옆에 ansible 1.9.x 설치 +(venv2)$ deactivate +$ source environment.1.9.sh + +# 플레이북 실행 +(venv1.9)$ ansible-playbook playbooks/ansible1.9_playbook.yml # 작동함! + +# venv1.9와 venv2가 모두 존재한다는 점에 유의하십시오 - 하나를 (비)활성화해야 합니다 - 그게 전부입니다. +``` + +#### become-user, become + +Ansible에서 `sudo`가 되려면 `become` 매개변수를 사용하십시오. 사용자 이름을 지정하려면 `become_user`를 사용하십시오. + +``` +- name: Ensure the httpd service is running + service: + name: httpd + state: started + become: true +``` + +참고: '관리자' 권한이 필요한 경우 감독되지 않은 실행을 허용하려면 `--ask-sudo-pass`로 Ansible을 실행하거나 sudoers 파일에 사용자를 추가해야 할 수 있습니다. + +[더 읽기](http://docs.ansible.com/ansible/latest/become.html) + +## 팁과 요령 + +#### --check -C + +플레이북이 '드라이 런' 모드(--check)에서 실행될 수 있고, 실행 시 '변경된' 개체를 선언하지 않는지 항상 확인하십시오. + +#### --diff -D + +Diff는 변경된 파일의 세부 정보를 보기 위해 유용합니다. +`diff -BbruN fileA fileB`와 같이 '메모리에서' 파일을 비교합니다. + + +#### '정규식'으로 호스트 실행 + +```bash +ansible -m ping web* +``` + +#### 호스트 그룹은 결합, 부정 등이 가능합니다. + +```bash +ansible -m ping web*:!backend:monitoring:&allow_change +``` + +#### 태그 지정 + +일부(전부는 아님) 개체(플레이북의 작업, 역할에서 포함된 모든 작업 등)를 태그 지정해야 합니다. 플레이북의 선택된 부분을 실행할 수 있습니다. + +#### no_logs: True + +일부 역할은 상세 모드에서 많은 출력을 인쇄하는 것을 볼 수 있습니다. 디버그 모듈도 있습니다. 자격 증명이 유출될 수 있는 곳입니다. 출력을 숨기려면 `no_log`를 사용하십시오. + +#### 디버그 모듈 + +화면에 값을 인쇄할 수 있습니다 - 사용하십시오! + +#### 작업의 출력 등록 + +`register` 명령으로 작업의 출력(stdout), rc(반환 코드), stderr을 등록할 수 있습니다. + +#### 조건부: when: + +#### 루프: with, with_items, with_dict, with_together + +[더 읽기](http://docs.ansible.com/ansible/latest/playbooks_conditionals.html) + +## 추가 자료 + +* [해커를 위한 서버: Ansible 튜토리얼](https://serversforhackers.com/c/an-ansible-tutorial) +* [시스템 관리자를 위한 Ansible 시작 가이드 - FAST!](https://www.redhat.com/en/blog/system-administrators-guide-getting-started-ansible-fast) +* [Ansible Tower](https://www.ansible.com/products/tower) - Ansible Tower는 ansible에 대한 웹 UI, 대시보드 및 나머지 인터페이스를 제공합니다. +* [Ansible AWX](https://github.com/ansible/awx) - Ansible Tower의 오픈 소스 버전. +* [초보자를 위한 Ansible 튜토리얼: 궁극의 플레이북 및 예제](https://spacelift.io/blog/ansible-tutorial) \ No newline at end of file diff --git a/ko/apl.md b/ko/apl.md new file mode 100644 index 0000000000..d4b7b253da --- /dev/null +++ b/ko/apl.md @@ -0,0 +1,71 @@ +--- +name: APL +contributors: + - ["nooodl", "https://github.com/nooodl"] +filename: learnapl.apl +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```apl +⍝ APL의 주석은 ⍝로 시작합니다. + +⍝ 숫자 목록. (¯는 음수입니다) +2 3e7 ¯4 50.3 + +⍝ 표현식, 몇 가지 함수를 보여줍니다. APL에는 +⍝ 연산 순서가 없습니다: 모든 것이 오른쪽에서 왼쪽으로 +⍝ 구문 분석됩니다. 이것은 5 + (4 × (2 ÷ (5 - 3))) = 9와 같습니다: +5 + 4 × 2 ÷ 5 - 3 ⍝ 9 + +⍝ 이 함수들은 목록에서도 작동합니다: +1 2 3 4 × 5 ⍝ 5 10 15 20 +1 2 3 4 × 5 6 7 8 ⍝ 5 12 21 32 + +⍝ 모든 함수는 단일 인수와 이중 인수 +⍝ 의미를 가집니다. 예를 들어, 두 인수에 적용된 "×"는 +⍝ 곱셈을 의미하지만, 오른쪽 +⍝ 면에만 적용될 때는 부호를 반환합니다: + +× ¯4 ¯2 0 2 4 ⍝ ¯1 ¯1 0 1 1 + +⍝ 값은 다음 연산자를 사용하여 비교할 수 있습니다 (1은 +⍝ "참", 0은 "거짓"을 의미합니다): + +10 20 30 = 10 20 99 ⍝ 1 1 0 + +10 20 30 < 10 20 99 ⍝ 0 0 1 + +⍝ "⍳n"은 처음 n개의 자연수를 포함하는 벡터를 반환합니다. +⍝ 행렬은 ⍴ (재구성)을 사용하여 구성할 수 있습니다: +4 3 ⍴ ⍳5 ⍝ 0 1 2 + ⍝ 3 4 0 + ⍝ 1 2 3 + ⍝ 4 0 1 + +⍝ 단일 인수 ⍴는 차원을 다시 제공합니다: +⍴ 4 3 ⍴ ⍳5 ⍝ 4 3 + +⍝ 값은 ←를 사용하여 저장할 수 있습니다. 숫자 벡터의 평균값을 +⍝ 계산해 보겠습니다: +A ← 10 60 55 23 + +⍝ A의 요소 합계 (/는 축소입니다): ++/A ⍝ 148 + +⍝ A의 길이: +⍴A ⍝ 4 + +⍝ 평균: +(+/A) ÷ (⍴A) ⍝ 37 + +⍝ 이것을 {}와 ⍵를 사용하여 함수로 정의할 수 있습니다: +mean ← {(+/⍵)÷⍴⍵} +mean A ⍝ 37 +``` + +## 더 읽을거리 + +- [APL 위키](https://aplwiki.com/) +- 제작자가 쓴 APL 책의 이전 버전: [Kenneth Iverson - A Programming Language](https://www.softwarepreservation.org/projects/apl/Books/APROGRAMMING%20LANGUAGE/view) +- 추가 도서: [APL Books](https://aplwiki.com/wiki/Books) diff --git a/ko/arturo.md b/ko/arturo.md new file mode 100644 index 0000000000..3513cbbb4f --- /dev/null +++ b/ko/arturo.md @@ -0,0 +1,438 @@ +--- +name: Arturo +filename: learnarturo.art +contributors: + - ["Dr.Kameleon", "https://github.com/drkameleon"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```red +; 이것은 주석입니다 +; 이것은 또 다른 주석입니다 + +;--------------------------------- +; 변수 및 값 +;--------------------------------- + +; 숫자 +a1: 2 +a2: 3.14 +a3: to :complex [1 2.0] ; 1.0+2.0i + +; 문자열 +c1: "이것은 문자열입니다" +c2: { + 이것은 여러 줄 문자열입니다 + 들여쓰기에 구애받지 않습니다 +} +c3: {: + 이것은 + 있는 그대로의 + 여러 줄 문자열입니다 + 정확히 + 원본과 같이 유지됩니다 +:} + +; 문자 +ch: `c` + +; 블록/배열 +d: [1 2 3] + +; 사전 +e: #[ + name: "John" + surname: "Doe" + age: 34 + likes: [pizza spaghetti] +] + +; 예, 함수도 값입니다 +f: function [x][ + 2 * x +] + +; 날짜 +g: now ; 2021-05-03T17:10:48+02:00 + +; 부울 +h1: true +h2: false + +;--------------------------------- +; 기본 연산자 +;--------------------------------- + +; 간단한 산술 +1 + 1 ; => 2 +8 - 1 ; => 7 +4.2 - 1.1 ; => 3.1 +10 * 2 ; => 20 +35 / 4 ; => 8 +35 // 4 ; => 8.75 +2 ^ 5 ; => 32 +5 % 3 ; => 2 + +; 비트 연산자 +and 3 5 ; => 1 +or 3 5 ; => 7 +xor 3 5 ; => 6 + +; 미리 정의된 상수 +pi ; => 3.141592653589793 +epsilon ; => 2.718281828459045 +null ; => null +true ; => true +false ; => false + +;--------------------------------- +; 비교 연산자 +;--------------------------------- + +; 같음 +1 = 1 ; => true +2 = 1 ; => false + +; 같지 않음 +1 <> 1 ; => false +2 <> 1 ; => true + +; 더 많은 비교 +1 < 10 ; => true +1 =< 10 ; => true +10 =< 10 ; => true +1 > 10 ; => false +1 >= 10 ; => false +11 >= 10 ; => true + +;--------------------------------- +; 조건문 +;--------------------------------- + +; 논리 연산자 +and? true true ; => true +and? true false ; => false +or? true false ; => true +or? false false ; => false + +and? [1=2][2<3] ; => false + ; (두 번째 블록은 평가되지 않습니다) + +; 간단한 if 문 +if 2 > 1 [ print "yes!"] ; yes! +if 3 <> 2 -> print "true!" ; true! + +; if/else 문 +if? 2 > 3 -> print "2 is greater than 3" +else -> print "2 is not greater than 3" ; 2 is not greater than 3 + +; switch 문 +switch 2 > 3 -> print "2 is greater than 3" + -> print "2 is not greater than 3" ; 2 is not greater than 3 + +a: (2 > 3)["yes"]["no"] ; a: "no" +a: (2 > 3)? -> "yes" -> "no" ; a: "no" (위와 정확히 동일) + +; case/when 문 +case [1] + when? [>2] -> print "1 is greater than 2. what?!" + when? [<0] -> print "1 is less than 0. nope..." + else -> print "here we are!" ; here we are! + +;--------------------------------- +; 루프 +;--------------------------------- + +; `loop` 사용 +arr: [1 4 5 3] +loop arr 'x [ + print ["x =" x] +] +; x = 1 +; x = 4 +; x = 5 +; x = 3 + +; loop와 사용자 정의 인덱스 사용 +loop.with:'i arr 'x [ + print ["item at position" i "=>" x] +] +; item at position 0 => 1 +; item at position 1 => 4 +; item at position 2 => 5 +; item at position 3 => 3 + +; 범위 사용 +loop 1..3 'x -> ; 단일 문장이므로 + print x ; [block] 표기법이 필요 없습니다 + ; `->` 구문 설탕을 사용하여 묶을 수 있습니다 + +loop `a`..`c` 'ch -> + print ch +; a +; b +; c + +; 여러 항목 선택 +loop 1..10 [x y] -> + print ["x =" x ", y =" y] +; x = 1 , y = 2 +; x = 3 , y = 4 +; x = 5 , y = 6 +; x = 7 , y = 8 +; x = 9 , y = 10 + +; 사전을 통한 루핑 +dict: #[ + name: "John" + surname: "Doe" + age: 34 +] +loop dict [key value][ + print [key "->" value] +] +; name -> John +; surname -> Doe +; age -> 34 + +; while 루프 +i: new 0 +while [i<3][ + print ["i =" i] + inc 'i +] +; i = 0 +; i = 1 +; i = 2 + +;--------------------------------- +; 문자열 +;--------------------------------- + +; 대소문자 +a: "tHis Is a stRinG" +print upper a ; THIS IS A STRING +print lower a ; this is a string +print capitalize a ; tHis Is a stRinG + +; 연결 +a: "Hello " ++ "World!" ; a: "Hello World!" + +; 배열로서의 문자열 +split "hello" ; => [h e l l o] +split.words "hello world" ; => [hello world] + +print first "hello" ; h +print last "hello" ; o + +; 변환 +to :string 123 ; => "123" +to :integer "123" ; => 123 + +; 문자열 결합 +join ["hello" "world"] ; => "helloworld" +join.with:"-" ["hello" "world"] ; => "hello-world" + +; 문자열 보간 +x: 2 +print ~"x = |x|" ; x = 2 + +; `print`를 사용한 보간 +print ["x =" x] ; x = 2 + ; (`print`는 주어진 블록을 계산하고 + ; 다른 값들을 문자열로 결합하여 + ; 사이에 단일 공백을 둡니다) + +; 템플릿 +print render.template { + <||= switch x=2 [ ||> + Yes, x = 2 + <||][||> + No, x is not 2 + <||]||> +} ; Yes, x = 2 + +; 일치 +prefix? "hello" "he" ; => true +suffix? "hello" "he" ; => false + +contains? "hello" "ll" ; => true +contains? "hello" "he" ; => true +contains? "hello" "x" ; => false + +in? "ll" "hello" ; => true +in? "x" "hello" ; => false + +;--------------------------------- +; 블록 +;--------------------------------- + +; 블록 계산 +arr: [1 1+1 1+1+1] +@arr ; => [1 2 3] + +; 블록 실행 +sth: [print "Hello world"] ; 이것은 완벽하게 유효하며, + ; *무엇이든* 포함할 수 있습니다 + ; 그리고 실행되지 않습니다... + +do sth ; Hello world + ; (...우리가 그렇게 하라고 말할 때까지) + +; 배열 인덱싱 +arr: ["zero" "one" "two" "three"] +print first arr ; zero +print arr\0 ; zero +print last arr ; three +print arr\3 ; three + +x: 2 +print get arr x ; two +print arr \ 2 ; two + ; (`get`에 대한 `\` 중위 별칭 사용 - + ; 피연산자 사이에 공백을 주목하십시오! + ; 그렇지 않으면 경로로 구문 분석됩니다) + +; 배열 요소 설정 +arr\0: "nada" +set arr 2 "dos" +print arr ; nada one dos three + +; 배열에 요소 추가 +arr: new [] +'arr ++ "one" +'arr ++ "two" +print arr ; one two + +; 배열에서 요소 제거 +arr: new ["one" "two" "three" "four"] +'arr -- "two" ; arr: ["one" "three" "four"] +remove 'arr .index 0 ; arr: ["three" "four"] + +; 배열 크기 가져오기 +arr: ["one" 2 "three" 4] +print size arr ; 4 + +; 배열의 일부 가져오기 +print slice ["one" "two" "three" "four"] 0 1 ; one two + +; 배열에 특정 요소가 포함되어 있는지 확인 +print contains? arr "one" ; true +print contains? arr "five" ; false + +; 배열 정렬 +arr: [1 5 3 2 4] +sort arr ; => [1 2 3 4 5] +sort.descending arr ; => [5 4 3 2 1] + +; 값 매핑 +map 1..10 [x][2*x] ; => [2 4 6 8 10 12 14 16 18 20] +map 1..10 'x -> 2*x ; 위와 동일 +map 1..10 => [2*&] ; 위와 동일 +map 1..10 => [2*] ; 위와 동일 + +; 배열 값 선택/필터링 +select 1..10 [x][odd? x] ; => [1 3 5 7 9] +select 1..10 => odd? ; 위와 동일 + +filter 1..10 => odd? ; => [2 4 6 8 10] + ; (이제 모든 홀수를 제외합니다 - + ; select는 유지하는 반면) + +; 기타 작업 +arr: ["one" 2 "three" 4] +reverse arr ; => [4 "three" 2 "one"] +shuffle arr ; => [2 4 "three" "one"] +unique [1 2 3 2 3 1] ; => [1 2 3] +permutate [1 2 3] ; => [[1 2 3] [1 3 2] [3 1 2] [2 1 3] [2 3 1] [3 2 1]] +take 1..10 3 ; => [1 2 3] +repeat [1 2] 3 ; => [1 2 1 2 1 2] + +;--------------------------------- +; 함수 +;--------------------------------- + +; 함수 선언 +f: function [x][ 2*x ] +f: function [x]-> 2*x ; 위와 동일 +f: $[x]->2*x ; 위와 동일 (단지 `function`에 대한 `$` 별칭 사용 + ; ... 함수) + +; 함수 호출 +f 10 ; => 20 + +; 값 반환 +g: function [x][ + if x < 2 -> return 0 + + res: 0 + loop 0..x 'z [ + res: res + z + ] + return res +] + +;--------------------------------- +; 사용자 정의 유형 +;--------------------------------- + +; 사용자 정의 유형 정의 +define :person [ + name ; 필드 포함: name, surname, age + surname + age +][ + ; 사용자 정의 사후 생성 초기화 프로그램 포함 + init: [ + this\name: capitalize this\name + ] + + ; 사용자 정의 인쇄 함수 + print: [ + render "NAME: |this\name|, SURNAME: |this\surname|, AGE: |this\age|" + ] + + ; 사용자 정의 비교 연산자 + compare: 'age +] + +; 사용자 정의 유형에 대한 메서드 생성 +sayHello: function [this][ + ensure -> is? :person this + + print ["Hello" this\name] +] + +; 사용자 정의 유형의 새 객체 생성 +a: to :person ["John" "Doe" 34] ; 2개의 "Person"을 생성해 보겠습니다 +b: to :person ["jane" "Doe" 33] ; 그리고 또 하나 + +; 의사 내부 메서드 호출 +sayHello a ; Hello John +sayHello b ; Hello Jane + +; 객체 필드 접근 +print ["The first person's name is:" a\name] ; The first person's name is: John +print ["The second person's name is:" b\name] ; The second person's name is: Jane + +; 객체 필드 변경 +a\name: "Bob" +sayHello a ; Hello Bob + +; 객체 유형 확인 +print type a ; :person +print is? :person a ; true + +; 객체 인쇄 +print a ; NAME: John, SURNAME: Doe, AGE: 34 + +; 사용자 객체 정렬 (사용자 정의 비교기 사용) +sort @[a b] ; Jane..., John... +sort.descending @[a b] ; John..., Jane... +``` + +## 추가 자료 + +- [공식 문서](https://arturo-lang.io/documentation/) - Arturo 공식 문서 및 참조. +- [온라인 놀이터](https://arturo-lang.io/playground/) - Arturo 프로그래밍 언어를 위한 온라인 REPL. diff --git a/ko/asciidoc.md b/ko/asciidoc.md new file mode 100644 index 0000000000..80881b9681 --- /dev/null +++ b/ko/asciidoc.md @@ -0,0 +1,133 @@ +--- +name: AsciiDoc +contributors: + - ["Ryan Mavilia", "http://unoriginality.rocks/"] + - ["Abel Salgado Romero", "https://twitter.com/abelsromero"] +filename: asciidoc.adoc +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +AsciiDoc은 마크다운과 유사한 마크업 언어로, 책에서 블로그에 이르기까지 모든 것에 사용할 수 있습니다. 2002년 Stuart Rackham이 만든 이 언어는 간단하지만 뛰어난 사용자 정의 기능을 제공합니다. + +문서 헤더 + +헤더는 선택 사항이며 빈 줄을 포함할 수 없습니다. 콘텐츠와 최소 한 줄 이상의 빈 줄로 구분되어야 합니다. + +제목만 + +``` += 문서 제목 + +문서의 첫 문장. +``` + +제목 및 저자 + +``` += 문서 제목 +First Last + +이 문서의 시작. +``` + +여러 저자 + +``` += 문서 제목 +John Doe ; Jane Doe; Black Beard + +여러 저자가 있는 문서의 시작. +``` + +개정판 줄 (저자 줄 필요) + +``` += Doc Title V1 +Potato Man +v1.0, 2016-01-13 + +칩에 대한 이 기사는 재미있을 것입니다. +``` + +단락 + +``` +단락에는 특별한 것이 필요하지 않습니다. + +단락을 구분하려면 단락 사이에 빈 줄을 추가하십시오. + +줄 바꿈을 만들려면 + +를 추가하면 줄 바꿈이 됩니다! +``` + +텍스트 서식 지정 + +``` +_밑줄은 기울임꼴을 만듭니다_ +*별표는 굵게* +*_재미를 더하기 위해 결합_* +`단일 인용 부호는 고정 폭을 나타냅니다` +`*굵은 고정 폭*` +``` + +섹션 제목 + +``` += 레벨 0 (문서 헤더에서만 사용할 수 있음) + +== 레벨 1

+ +=== 레벨 2

+ +==== 레벨 3

+ +===== 레벨 4

+``` + +목록 + +글머리 기호 목록을 만들려면 별표를 사용하십시오. + +``` +* foo +* bar +* baz +``` + +번호 매기기 목록을 만들려면 마침표를 사용하십시오. + +``` +. 항목 1 +. 항목 2 +. 항목 3 +``` + +최대 5번까지 추가 별표나 마침표를 추가하여 목록을 중첩할 수 있습니다. + +``` +* foo 1 +** foo 2 +*** foo 3 +**** foo 4 +***** foo 5 + +. foo 1 +.. foo 2 +... foo 3 +.... foo 4 +..... foo 5 +``` + +## 더 읽을거리 + +AsciiDoc 문서를 처리하는 두 가지 도구가 있습니다: + +1. [AsciiDoc](http://asciidoc.org/): 주요 Linux 배포판에서 사용할 수 있는 원래 Python 구현입니다. 안정적이며 현재 유지 관리 모드입니다. +2. [Asciidoctor](http://asciidoctor.org/): Java 및 JavaScript에서도 사용할 수 있는 대체 Ruby 구현입니다. 활발하게 개발 중이며 새로운 기능과 출력 형식으로 AsciiDoc 구문을 확장하는 것을 목표로 합니다. + +다음 링크는 `Asciidoctor` 구현과 관련이 있습니다: + +* [마크다운 - AsciiDoc 구문 비교](http://asciidoctor.org/docs/user-manual/#comparison-by-example): 일반적인 마크다운 및 AsciiDoc 요소의 나란히 비교. +* [시작하기](http://asciidoctor.org/docs/#get-started-with-asciidoctor): 간단한 문서를 렌더링하기 위한 설치 및 빠른 시작 가이드. +* [Asciidoctor 사용자 설명서](http://asciidoctor.org/docs/user-manual/): 구문 참조, 예제, 렌더링 도구 등이 포함된 완전한 단일 문서 설명서. \ No newline at end of file diff --git a/ko/assemblyscript.md b/ko/assemblyscript.md new file mode 100644 index 0000000000..9672c4cd4c --- /dev/null +++ b/ko/assemblyscript.md @@ -0,0 +1,199 @@ +--- +name: AssemblyScript +contributors: + - ["Philippe Vlérick", "https://github.com/pvlerick"] + - ["Steve Huguenin-Elie", "https://github.com/StEvUgnIn"] + - ["Sebastian Speitel", "https://github.com/SebastianSpeitel"] + - ["Max Graey", "https://github.com/MaxGraey"] +filename: learnassemblyscript.ts +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +__AssemblyScript__ 는 __TypeScript__ 의 변형(기본적으로 타입이 있는 자바스크립트)을 __Binaryen__ 을 사용하여 __WebAssembly__ 로 컴파일합니다. `npm install`만으로 간결하고 효율적인 WebAssembly 모듈을 생성합니다. + +이 문서는 [TypeScript](../typescript/) 및 [JavaScript](../javascript/)와 대조적으로 AssemblyScript의 추가 구문에만 초점을 맞춥니다. + +AssemblyScript의 컴파일러를 테스트하려면 +[Playground](https://www.assemblyscript.org/editor.html#IyFydW50aW1lPXN0dWIKLyoqIENhbGN1bGF0ZXMgdGhlIG4tdGggRmlib25hY2NpIG51bWJlci4gKi8KZXhwb3J0IGZ1bmN0aW9uIGZpYihuOiBpMzIpOiBpMzIgewogIHZhciBhID0gMCwgYiA9IDEKICBpZiAobiA+IDApIHsKICAgIHdoaWxlICgtLW4pIHsKICAgICAgbGV0IHQgPSBhICsgYgogICAgICBhID0gYgogICAgICBiID0gdAogICAgfQogICAgcmV0dXJuIGIKICB9CiAgcmV0dXJuIGEKfQoKIyFodG1sCjx0ZXh0YXJlYSBpZD0ib3V0cHV0IiBzdHlsZT0iaGVpZ2h0OiAxMDAlOyB3aWR0aDogMTAwJSIgcmVhZG9ubHk+PC90ZXh0YXJlYT4KPHNjcmlwdD4KbG9hZGVyLmluc3RhbnRpYXRlKG1vZHVsZV93YXNtLCB7IC8qIGltcG9ydHMgKi8gfSkKICAudGhlbigoeyBleHBvcnRzIH0pID0+IHsKICAgIGNvbnN0IG91dHB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdvdXRwdXQnKQogICAgZm9yIChsZXQgaSA9IDA7IGkgPD0gMTA7ICsraSkgewogICAgICBvdXRwdXQudmFsdWUgKz0gYGZpYigke2l9KSA9ICR7ZXhwb3J0cy5maWIoaSl9XG5gCiAgICB9CiAgfSkKPC9zY3JpcHQ+Cg==)로 이동하여 코드를 입력하고 자동 완성을 사용하며 내보낸 WebAssembly를 직접 볼 수 있습니다. + +```ts +// AssemblyScript에는 많은 기본 타입이 있습니다. +let isDone: boolean = false; +let name: string = "Anders"; + +// 하지만 정수 타입은 부호 있는(8비트에서 64비트까지 크기)으로 제공됩니다. +let lines8: i8 = 42; +let lines16: i16 = 42; +let lines32: i32 = 42; +let lines64: i64 = 42; + +// 그리고 부호 없는(8비트에서 64비트까지 크기)으로 제공됩니다. +let ulines8: u8 = 42; +let ulines16: u16 = 42; +let ulines32: u32 = 42; +let ulines64: u64 = 42; + +// 그리고 float는 두 가지 크기(32/64)가 가능합니다. +let rate32: f32 = 1.0 +let rate64: f64 = 1.0 + +// 하지만 변수가 명시적인 리터럴에서 파생된 경우 타입 주석을 생략할 수 있습니다. +let _isDone = false; +let _lines = 42; +let _name = "Anders"; + +// 상수에 const 키워드를 사용합니다. +const numLivesForCat = 9; +numLivesForCat = 1; // 오류 + +// 컬렉션의 경우 타입이 지정된 배열과 제네릭 배열이 있습니다. +let list1: i8[] = [1, 2, 3]; +// 또는 제네릭 배열 타입을 사용합니다. +let list2: Array = [1, 2, 3]; + +// 열거형의 경우: +enum Color { Red, Green, Blue }; +let c: Color = Color.Green; + +// 자바스크립트에서 가져온 함수는 외부로 선언해야 합니다. +// @ts-ignore 데코레이터 +@external("alert") +declare function alert(message: string): void; + +// 그리고 네임스페이스에서 JS 함수를 가져올 수도 있습니다. +declare namespace window { + // @ts-ignore 데코레이터 + @external("window", "alert") + function alert(message: string): void; +} + +// 마지막으로, "void"는 함수가 아무것도 반환하지 않는 특별한 경우에 사용됩니다. +export function bigHorribleAlert(): void { + alert("I'm a little annoying box!"); // 여기서 JS 함수 호출 +} + +// 함수는 일급 시민이며, 람다 "뚱뚱한 화살표" 구문을 지원합니다. + +// 다음은 동일하며, 컴파일러는 아직 함수에 대한 타입 추론을 제공하지 않으며 +// 동일한 WebAssembly가 내보내집니다. +export function f1 (i: i32): i32 { return i * i; } +// "뚱뚱한 화살표" 구문 +let f2 = (i: i32): i32 => { return i * i; } +// "뚱뚱한 화살표" 구문, 중괄호가 없으면 return 키워드가 필요하지 않음을 의미합니다. +let f3 = (i: i32): i32 => i * i; + +// 클래스 - 멤버는 기본적으로 public입니다. +export class Point { + // 속성 + x: f64; + + // 생성자 - 이 컨텍스트의 public/private 키워드는 + // 속성에 대한 상용구 코드와 생성자의 초기화를 생성합니다. + // 이 예제에서 "y"는 "x"와 같이 정의되지만 코드는 더 적습니다. + // 기본값도 지원됩니다. + + constructor(x: f64, public y: f64 = 0) { + this.x = x; + } + + // 함수 + dist(): f64 { return Math.sqrt(this.x * this.x + this.y * this.y); } + + // 정적 멤버 + static origin: Point = new Point(0, 0); +} + +// 클래스는 부모 클래스를 확장한다고 명시적으로 표시할 수 있습니다. +// 누락된 속성은 컴파일 타임에 오류를 발생시킵니다. +export class PointPerson extends Point { + constructor(x: f64, y: f64, public name: string) { + super(x, y); + } + move(): void {} +} + +let p1 = new Point(10, 20); +let p2 = new Point(25); //y는 0이 됩니다. + +// 상속 +export class Point3D extends Point { + constructor(x: f64, y: f64, public z: f64 = 0) { + super(x, y); // 슈퍼 클래스 생성자에 대한 명시적 호출은 필수입니다. + } + + // 덮어쓰기 + dist(): f64 { + let d = super.dist(); + return Math.sqrt(d * d + this.z * this.z); + } +} + +// 네임스페이스, "."는 하위 네임스페이스의 구분 기호로 사용할 수 있습니다. +export namespace Geometry { + class Square { + constructor(public sideLength: f64 = 0) { + } + area(): f64 { + return Math.pow(this.sideLength, 2); + } + } +} + +let s1 = new Geometry.Square(5); + +// 제네릭 +// AssemblyScript는 제네릭을 고유한 컨텍스트 타입 인수 집합당 하나의 구체적인 메서드 또는 함수로 컴파일합니다. +// 이를 [단형화]라고도 합니다. +// 이는 모듈이 실제로 사용되는 타입 인수 집합에 대한 구체적인 함수만 포함하고 내보내며 +// 구체적인 함수는 컴파일 타임에 [정적 타입 검사]로 단축될 수 있음을 의미하며, 이는 매우 유용한 것으로 판명되었습니다. +// 클래스 +export class Tuple { + constructor(public item1: T1, public item2: T2) { + } +} + +export class Pair { + item1: T; + item2: T; +} + +// 그리고 함수 +export function pairToTuple (p: Pair): Tuple { + return new Tuple(p.item1, p.item2); +}; + +let tuple = pairToTuple({ item1: "hello", item2: "world" }); + +// TypeScript 전용 정의 파일에 대한 참조 포함: +/// + +// 템플릿 문자열 (백틱을 사용하는 문자열) +// 템플릿 문자열을 사용한 문자열 보간 +let name = 'Tyrone'; +let greeting = `Hi ${name}, how are you?` +// 템플릿 문자열을 사용한 여러 줄 문자열 +let multiline = `This is an example +of a multiline string`; + +let numbers: Array = [0, 1, 2, 3, 4]; +let moreNumbers: Array = numbers; +moreNumbers[5] = 5; // 오류, 요소는 읽기 전용입니다. +moreNumbers.push(5); // 오류, push 메서드 없음 (배열을 변경하기 때문) +moreNumbers.length = 3; // 오류, 길이는 읽기 전용입니다. +numbers = moreNumbers; // 오류, 변경 메서드가 없습니다. + +// 배열의 타입 추론 +let ints = [0, 1, 2, 3, 4] // Array로 추론됩니다. +let floats: f32[] = [0, 1, 2, 3, 4] // Array로 추론됩니다. +let doubles = [0.0, 1.0, 2, 3, 4] // Array로 추론됩니다. +let bytes1 = [0 as u8, 1, 2, 3, 4] // Array로 추론됩니다. +let bytes2 = [0, 1, 2, 3, 4] as u8[] // Array로 추론됩니다. +let bytes3: u8[] = [0, 1, 2, 3, 4] // Array로 추론됩니다. +``` + +## 더 읽을거리 + + * [AssemblyScript 공식 웹사이트](https://www.assemblyscript.org/) + * [AssemblyScript 소스 문서](https://github.com/AssemblyScript/website/tree/main/src) + * [GitHub의 소스 코드](https://github.com/AssemblyScript/assemblyscript) \ No newline at end of file diff --git a/ko/asymptotic-notation.md b/ko/asymptotic-notation.md new file mode 100644 index 0000000000..e7bad8aa4e --- /dev/null +++ b/ko/asymptotic-notation.md @@ -0,0 +1,141 @@ +--- +category: Algorithms & Data Structures +name: Asymptotic Notation +contributors: + - ["Jake Prather", "http://github.com/JakeHP"] + - ["Divay Prakash", "http://github.com/divayprakash"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- +# 점근 표기법 + +## 점근 표기법이란? + +점근 표기법은 알고리즘의 입력 크기가 증가함에 따라 동작을 식별하여 알고리즘의 실행 시간을 분석할 수 있는 언어입니다. 이를 알고리즘의 성장률이라고도 합니다. 입력 크기가 커지면 알고리즘이 갑자기 엄청나게 느려지나요? 입력 크기가 증가해도 빠른 실행 시간을 대부분 유지하나요? 점근 표기법은 이러한 질문에 답할 수 있는 능력을 제공합니다. + +## 이러한 질문에 답할 수 있는 대안이 있나요? + +한 가지 방법은 다양한 입력 크기에서 기본 연산의 수를 세는 것입니다. 이것이 유효한 해결책이기는 하지만, 간단한 알고리즘에 대해서도 이 작업에 드는 노력이 그 사용을 정당화하지 못합니다. + +또 다른 방법은 다양한 입력 크기가 주어졌을 때 알고리즘이 완료되는 데 걸리는 시간을 물리적으로 측정하는 것입니다. 그러나 이 방법의 정확성과 상대성(얻은 시간은 계산된 컴퓨터에만 상대적임)은 컴퓨터 하드웨어 사양, 처리 능력 등과 같은 환경 변수에 의해 제한됩니다. + +## 점근 표기법의 종류 + +이 문서의 첫 번째 섹션에서는 점근 표기법이 입력 크기가 변경될 때 알고리즘의 동작을 식별하는 방법을 설명했습니다. 알고리즘을 함수 f, 입력 크기를 n, f(n)을 실행 시간이라고 상상해 봅시다. 따라서 주어진 알고리즘 f에 대해 입력 크기 n이 주어지면 결과 실행 시간 f(n)을 얻습니다. 이로 인해 Y축이 실행 시간, X축이 입력 크기, 플롯 포인트가 주어진 입력 크기에 대한 시간의 결과인 그래프가 생성됩니다. + +다양한 방법으로 함수 또는 알고리즘에 점근 표기법으로 레이블을 지정할 수 있습니다. 몇 가지 예로, 최상의 경우, 최악의 경우 또는 평균적인 경우로 알고리즘을 설명할 수 있습니다. 가장 일반적인 것은 최악의 경우로 알고리즘을 분석하는 것입니다. 일반적으로 최상의 경우로 평가하지는 않습니다. 왜냐하면 그러한 조건은 계획하는 것이 아니기 때문입니다. 이에 대한 훌륭한 예는 정렬 알고리즘입니다. 특히 트리 구조에 요소를 추가하는 것입니다. 대부분의 알고리즘에 대한 최상의 경우는 단일 연산만큼 낮을 수 있습니다. 그러나 대부분의 경우 추가하는 요소는 트리를 통해 적절하게 정렬되어야 하며, 이는 전체 분기를 검사해야 함을 의미할 수 있습니다. 이것이 최악의 경우이며, 이것이 우리가 계획하는 것입니다. + +### 함수, 한계 및 단순화의 종류 + +``` +로그 함수 - log n +선형 함수 - an + b +이차 함수 - an^2 + bn + c +다항 함수 - an^z + . . . + an^2 + a*n^1 + a*n^0, 여기서 z는 상수입니다. +지수 함수 - a^n, 여기서 a는 상수입니다. +``` + +이들은 다양한 표기법에서 사용되는 몇 가지 기본적인 함수 성장 분류입니다. 목록은 가장 느리게 성장하는 함수(로그, 가장 빠른 실행 시간)에서 시작하여 가장 빠르게 성장하는 함수(지수, 가장 느린 실행 시간)로 이어집니다. 'n' 또는 입력이 각 함수에서 증가함에 따라 결과가 로그 및 선형에 비해 이차, 다항 및 지수에서 훨씬 빠르게 증가한다는 점에 유의하십시오. + +논의될 표기법에 대해 가능한 한 가장 간단한 용어를 사용하는 것이 가장 좋습니다. 이는 상수 및 낮은 차수의 항을 무시한다는 것을 의미합니다. 왜냐하면 입력 크기(또는 f(n) 예제의 n)가 무한대로 증가함에 따라(수학적 한계), 낮은 차수의 항과 상수는 거의 또는 전혀 중요하지 않기 때문입니다. 즉, 2^9001 또는 상상할 수 없는 다른 엄청난 양의 상수가 있는 경우 단순화하면 표기법 정확도가 왜곡될 수 있다는 것을 깨달으십시오. + +가장 간단한 형태를 원하므로 표를 약간 수정해 보겠습니다. + +``` +로그 - log n +선형 - n +이차 - n^2 +다항 - n^z, 여기서 z는 상수입니다. +지수 - a^n, 여기서 a는 상수입니다. +``` + +### 빅오 +빅오는 일반적으로 **O**로 표기되며, 주어진 함수에 대한 최악의 경우 또는 성장 상한에 대한 점근 표기법입니다. 알고리즘의 실행 시간 성장률에 대한 _**점근적 상한**_을 제공합니다. `f(n)`이 알고리즘 실행 시간이고 `g(n)`이 알고리즘과 관련시키려는 임의의 시간 복잡도라고 가정합니다. `f(n)`은 O(g(n))이며, 일부 실수 상수 c(c > 0) 및 n0에 대해 모든 입력 크기 n(n > n0)에 대해 `f(n)` <= `c g(n)`입니다. + +*예제 1* + +``` +f(n) = 3log n + 100 +g(n) = log n +``` + +`f(n)`은 O(g(n))인가요? +`3 log n + 100`은 O(log n)인가요? +빅오의 정의를 살펴보겠습니다. + +``` +3log n + 100 <= c * log n +``` + +모든 n > n0에 대해 이를 만족하는 상수 c, n0 쌍이 있나요? + +``` +3log n + 100 <= 150 * log n, n > 2 (n = 1에서 정의되지 않음) +``` + +예! 빅오의 정의가 충족되었으므로 `f(n)`은 O(g(n))입니다. + +*예제 2* + +``` +f(n) = 3*n^2 +g(n) = n +``` + +`f(n)`은 O(g(n))인가요? +`3 * n^2`은 O(n)인가요? +빅오의 정의를 살펴보겠습니다. + +``` +3 * n^2 <= c * n +``` + +모든 n > n0에 대해 이를 만족하는 상수 c, n0 쌍이 있나요? +아니요, 없습니다. `f(n)`은 O(g(n))이 아닙니다. + +### 빅오메가 +빅오메가는 일반적으로 **Ω**로 표기되며, 주어진 함수에 대한 최상의 경우 또는 성장률 하한에 대한 점근 표기법입니다. 알고리즘의 실행 시간 성장률에 대한 _**점근적 하한**_을 제공합니다. + +`f(n)`은 Ω(g(n))이며, 일부 실수 상수 c(c > 0) 및 n0(n0 > 0)에 대해 모든 입력 크기 n(n > n0)에 대해 `f(n)` >= `c g(n)`입니다. + +### 참고 + +빅오 및 빅오메가 표기법으로 제공되는 점근적 성장률은 점근적으로 엄격할 수도 있고 그렇지 않을 수도 있습니다. 따라서 점근적으로 엄격하지 않은 경계를 나타내기 위해 스몰오 및 스몰오메가 표기법을 사용합니다. + +### 스몰오 +스몰오는 일반적으로 **o**로 표기되며, 알고리즘의 실행 시간 성장률에 대한 상한(점근적으로 엄격하지 않음)을 나타내는 점근 표기법입니다. + +`f(n)`은 o(g(n))이며, 모든 실수 상수 c(c > 0) 및 n0(n0 > 0)에 대해 모든 입력 크기 n(n > n0)에 대해 `f(n)` < `c g(n)`입니다. + +O-표기법과 o-표기법의 정의는 유사합니다. 주요 차이점은 f(n) = O(g(n))에서 경계 f(n) <= g(n)은 _**일부**_ 상수 c > 0에 대해 유지되지만, f(n) = o(g(n))에서 경계 f(n) < c g(n)은 _**모든**_ 상수 c > 0에 대해 유지된다는 것입니다. + +### 스몰오메가 +스몰오메가는 일반적으로 **ω**로 표기되며, 알고리즘의 실행 시간 성장률에 대한 하한(점근적으로 엄격하지 않음)을 나타내는 점근 표기법입니다. + +`f(n)`은 ω(g(n))이며, 모든 실수 상수 c(c > 0) 및 n0(n0 > 0)에 대해 모든 입력 크기 n(n > n0)에 대해 `f(n)` > `c g(n)`입니다. + +Ω-표기법과 ω-표기법의 정의는 유사합니다. 주요 차이점은 f(n) = Ω(g(n))에서 경계 f(n) >= g(n)은 _**일부**_ 상수 c > 0에 대해 유지되지만, f(n) = ω(g(n))에서 경계 f(n) > c g(n)은 _**모든**_ 상수 c > 0에 대해 유지된다는 것입니다. + +### 세타 +세타는 일반적으로 **Θ**로 표기되며, 알고리즘의 실행 시간 성장률에 대한 _**점근적으로 엄격한 경계**_를 나타내는 점근 표기법입니다. + +`f(n)`은 Θ(g(n))이며, 일부 실수 상수 c1, c2 및 n0(c1 > 0, c2 > 0, n0 > 0)에 대해 모든 입력 크기 n(n > n0)에 대해 `c1 g(n)` < `f(n)` < `c2 g(n)`입니다. + +∴ `f(n)`이 Θ(g(n))이면 `f(n)`은 O(g(n))이고 `f(n)`은 Ω(g(n))입니다. + +이에 대한 예는 추가 자료를 참조하십시오. 빅오는 일반적인 알고리즘 시간 복잡도에 사용되는 기본 표기법입니다. + +### 끝맺음 + +이러한 종류의 주제를 짧게 유지하기는 어렵습니다. 나열된 책과 온라인 자료를 통해 정의와 예제에 대해 훨씬 더 깊이 있게 다루고 있습니다. x='알고리즘 및 데이터 구조'인 곳에 더 많은 내용이 있습니다. 곧 실제 코드 예제를 분석하는 문서가 올라올 것입니다. + +## 도서 + +* [알고리즘](http://www.amazon.com/Algorithms-4th-Robert-Sedgewick/dp/032157351X) +* [알고리즘 설계](http://www.amazon.com/Algorithm-Design-Foundations-Analysis-Internet/dp/0471383651) + +## 온라인 자료 + +* [MIT](http://web.mit.edu/16.070/www/lecture/big_o.pdf) +* [칸아카데미](https://www.khanacademy.org/computing/computer-science/algorithms/asymptotic-notation/a/asymptotic-notation) +* [빅오 치트시트](http://bigocheatsheet.com/) - 복잡도별로 순위가 매겨진 일반적인 구조, 연산 및 알고리즘. \ No newline at end of file diff --git a/ko/ats.md b/ko/ats.md new file mode 100644 index 0000000000..1b8273a94b --- /dev/null +++ b/ko/ats.md @@ -0,0 +1,592 @@ +--- +name: ATS +contributors: + - ["Mark Barbone", "https://github.com/mb64"] +filename: learnats.dats +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +ATS는 저수준 함수형 프로그래밍 언어입니다. C와 동일한 수준의 제어 및 효율성으로 프로그램을 작성할 수 있는 강력한 타입 시스템을 갖추고 있지만, 메모리 안전 및 타입 안전 방식으로 작성할 수 있습니다. + +ATS 타입 시스템은 다음을 지원합니다: + +* 전체 타입 삭제: ATS는 효율적인 C로 컴파일됩니다. +* 종속 타입, [LF](http://twelf.org/wiki/LF) 및 메타정리 증명 포함 +* 정제 타입 +* 리소스 추적을 위한 선형성 +* 예외, 변경, 종료 및 기타 부작용을 추적하는 효과 시스템 + +이 튜토리얼은 함수형 프로그래밍, 종속 타입 또는 선형 타입에 대한 소개가 아니라 ATS에서 이 모든 것이 어떻게 함께 작동하는지에 대한 것입니다. 즉, ATS는 매우 복잡한 언어이며 이 튜토리얼에서는 모든 것을 다루지 않습니다. ATS의 타입 시스템은 혼란스러운 기능을 광범위하게 자랑할 뿐만 아니라, 특이한 구문으로 인해 "간단한" 예제조차 이해하기 어려울 수 있습니다. 합리적인 길이를 유지하기 위해 이 문서는 가능한 것과 방법에 대한 높은 수준의 개요를 제공하여 ATS의 맛을 보여주는 것을 목표로 하며, 모든 것이 어떻게 작동하는지 완전히 설명하려고 시도하지는 않습니다. + +[브라우저에서 ATS를 사용해 보거나](http://www.ats-lang.org/SERVER/MYCODE/Patsoptaas_serve.php) [http://www.ats-lang.org/](http://www.ats-lang.org/)에서 설치할 수 있습니다. + + +```ocaml +// 표준 라이브러리 포함 +#include "share/atspre_define.hats" +#include "share/atspre_staload.hats" + +// 컴파일하려면 다음 중 하나를 사용하십시오. +// $ patscc -DATS_MEMALLOC_LIBC program.dats -o program +// 또는 ats-acc 래퍼 https://github.com/sparverius/ats-acc를 설치하고 다음을 사용하십시오. +// $ acc pc program.dats + +// C 스타일 줄 주석 +/* 및 C 스타일 블록 주석 */ +(* ML 스타일 블록 주석도 마찬가지입니다 *) + +/*************** 1부: ML 조각 ****************/ + +val () = print "Hello, World!\n" + +// 커링 없음 +fn add (x: int, y: int) = x + y + +// fn 대 fun은 OCaml/F#에서 let과 let rec의 차이와 같습니다. +fun fact (n: int): int = if n = 0 then 1 else n * fact (n-1) + +// 다중 인수 함수는 호출할 때 괄호가 필요합니다. 단일 인수 +// 함수는 괄호를 생략할 수 있습니다. +val forty_three = add (fact 4, 19) + +// let은 SML의 let과 같습니다. +fn sum_and_prod (x: int, y: int): (int, int) = + let + val sum = x + y + val prod = x * y + in (sum, prod) end + +// 'type'은 힙에 할당된 모든 비선형 타입의 타입입니다. +// 다형성 매개변수는 함수 이름 뒤에 {}로 들어갑니다. +fn id {a:type} (x: a) = x + +// int는 힙에 할당되지 않으므로 'id'에 전달할 수 없습니다. +// val y: int = id 7 // 컴파일되지 않음 + +// 't@ype'은 잠재적으로 언박싱된 모든 비선형 타입의 타입입니다. 'type'의 +// 상위 타입입니다. +// 템플릿 매개변수는 함수 이름 앞에 {}로 들어갑니다. +fn {a:t@ype} id2 (x: a) = x + +val y: int = id2 7 // 작동함 + +// 다형성 t@ype 매개변수를 가질 수 없습니다. +// fn id3 {a:t@ype} (x: a) = x // 컴파일되지 않음 + +// 타입 매개변수 명시적 지정: +fn id4 {a:type} (x: a) = id {a} x // 비템플릿 매개변수에 대한 {} +fn id5 {a:type} (x: a) = id2 x // 템플릿 매개변수에 대한 <> +fn id6 {a:type} (x: a) = id {..} x // 명시적으로 추론하기 위한 {..} + +// 힙 할당 공유 데이터 타입 +// 데이터 타입을 사용하면 메모리가 누수됩니다. +datatype These (a:t@ype, b:t@ype) = This of a + | That of b + | These of (a, b) + +// 'case'를 사용한 패턴 매칭 +fn {a,b: t@ype} from_these (x: a, y: b, these: These(a,b)): (a, b) = + case these of + | This(x) => (x, y) // 변수 이름의 섀도잉은 괜찮습니다. 여기서 x는 + // 매개변수 x를 섀도잉합니다. + | That(y) => (x, y) + | These(x, y) => (x, y) + +// 'case-'를 사용한 부분 패턴 매칭 +// This를 전달하면 예외가 발생합니다. +fn {a,b:t@ype} unwrap_that (these: These(a,b)): b = + case- these of + | That(y) => y + | These(_, y) => y + + +/*************** 2부: 정제 ****************/ + +// 함수가 취하고 반환하는 값으로 함수를 매개변수화합니다. +fn cool_add {n:int} {m:int} (x: int n, y: int m): int (n+m) = x + y + +// list(a, n)은 n개의 a로 구성된 목록입니다. +fun square_all {n:int} (xs: list(int, n)): list(int, n) = + case xs of + | list_nil() => list_nil() + | list_cons(x, rest) => list_cons(x * x, square_all rest) + +fn {a:t@ype} get_first {n:int | n >= 1} (xs: list(a, n)): a = + case+ xs of // '+'는 ATS에게 전체임을 증명하도록 요청합니다. + | list_cons(x, _) => x + +// 길이가 0인 목록에서 get_first를 실행할 수 없습니다. +// val x: int = get_first (list_nil()) // 컴파일되지 않음 + +// stdlib에서: +// sortdef nat = {n:int | n >= 0} +// sortdef pos = {n:int | n >= 1} + +fn {a:t@ype} also_get_first {n:pos} (xs: list(a, n)): a = + let + val+ list_cons(x, _) = xs // val+도 작동합니다. + in x end + +// 꼬리 재귀 역순 +fun {a:t@ype} reverse {n:int} (xs: list(a, n)): list(a, n) = + let + // 지역 함수는 둘러싸는 범위의 타입 변수를 사용할 수 있습니다. + // 이 함수는 'a'와 'n'을 모두 사용합니다. + fun rev_helper {i:nat} (xs: list(a, n-i), acc: list(a, i)): list(a, n) = + case xs of + | list_nil() => acc + | list_cons(x, rest) => rev_helper(rest, list_cons(x, acc)) + in rev_helper(xs, list_nil) end + +// ATS에는 세 가지 컨텍스트 종속 네임스페이스가 있습니다. +// 이 예제에서 두 개의 'int'는 두 개의 'n'과 마찬가지로 다른 것을 의미합니다. +fn namespace_example {n:int} (n: int n): int n = n +// ^^^ 정렬 네임스페이스 +// ^ ^^^ ^ ^^^ ^ 정적 네임스페이스 +// ^^^^^^^^^^^^^^^^^ ^ ^ 값 네임스페이스 + +// 종료 메트릭은 .< >.에 들어갈 수 있습니다. +// 각 재귀 호출에서 감소해야 합니다. +// 그러면 ATS는 무한 재귀가 아님을 증명합니다. +fun terminating_factorial {n:nat} .. (n: int n): int = + if n = 0 then 1 else n * terminating_factorial (n-1) + + +/*************** 3부: LF 조각 ****************/ + +// ATS는 LF(http://twelf.org/wiki/LF)에서 정리 증명을 지원합니다. + +// 관계는 귀납적 타입으로 표현됩니다. + +// n번째 피보나치 수가 f라는 증명 +dataprop Fib(n:int, m:int) = + | FibZero(0, 0) + | FibOne(1, 1) + | {n, f1, f2: int} FibInd(n, f1 + f2) of (Fib(n-1,f1), Fib(n-2,f2)) + +// 증명된 올바른 피보나치 구현 +// [A] B는 실존 타입입니다: "B인 A가 존재합니다" +// (증명 | 값) +fun fib {n:nat} .. (n: int n): [f:int] (Fib(n,f) | int f) = + if n = 0 then (FibZero | 0) else + if n = 1 then (FibOne | 1) else + let + val (proof1 | val1) = fib (n-1) + val (proof2 | val2) = fib (n-2) + // 실존 타입이 추론됩니다. + in (FibInd(proof1, proof2) | val1 + val2) end + +// 더 빠른 증명된 올바른 피보나치 구현 +fn fib_tail {n:nat} (n: int n): [f:int] (Fib(n,f) | int f) = + let + fun loop {i:int | i < n} {f1, f2: int} .. + (p1: Fib(i,f1), p2: Fib(i+1,f2) + | i: int i, f1: int f1, f2: int f2, n: int n + ): [f:int] (Fib(n,f) | int f) = + if i = n - 1 + then (p2 | f2) + else loop (p2, FibInd(p2,p1) | i+1, f2, f1+f2, n) + in if n = 0 then (FibZero | 0) else loop (FibZero, FibOne | 0, 0, 1, n) end + +// 증명 수준의 int 목록, 타입 'sort' +datasort IntList = ILNil of () + | ILCons of (int, IntList) + +// ILAppend(x,y,z) iff x ++ y = z +dataprop ILAppend(IntList, IntList, IntList) = + | {y:IntList} AppendNil(ILNil, y, y) + | {a:int} {x,y,z: IntList} + AppendCons(ILCons(a,x), y, ILCons(a,z)) of ILAppend(x,y,z) + +// prfuns/prfns는 증명에 작용하는 컴파일 타임 함수입니다. + +// 메타정리: append는 전체입니다. +prfun append_total {x,y: IntList} .. (): [z:IntList] ILAppend(x,y,z) + = scase x of // scase를 사용하면 정적 인수를 검사할 수 있습니다(prfun에서만). + | ILNil() => AppendNil + | ILCons(a,rest) => AppendCons(append_total()) + + +/*************** 4부: 뷰 ****************/ + +// 뷰는 소품과 같지만 선형입니다. 즉, 정확히 한 번 소비되어야 합니다. +// 소품은 뷰의 하위 타입입니다. + +// 'type @ address'는 가장 기본적인 뷰입니다. + +fn {a:t@ype} read_ptr {l:addr} (pf: a@l | p: ptr l): (a@l | a) = + let + // !p는 해당 주소에 무언가가 있다는 사용 가능한 증명을 검색합니다. + val x = !p + in (pf | x) end + +// 이런, 잠재적으로 유효하지 않은 포인터를 역참조하려고 했습니다. +// fn {a:t@ype} bad {l:addr} (p: ptr l): a = !p // 컴파일되지 않음 + +// 이런, 증명을 떨어뜨렸습니다(메모리 누수). +// fn {a:t@ype} bad {l:addr} (pf: a@l | p: ptr l): a = !p // 컴파일되지 않음 + +fn inc_at_ptr {l:addr} (pf: int@l | p: ptr l): (int@l | void) = + let + // !p := 값은 p의 위치에 값을 씁니다. + // !p와 마찬가지로 범위에 있는 사용 가능한 증명을 암시적으로 검색합니다. + val () = !p := !p + 1 + in (pf | ()) end + +// 증명을 스레딩하는 것은 짜증납니다. +fn inc_three_times {l:addr} (pf: int@l | p: ptr l): (int@l | void) = + let + val (pf2 | ()) = inc_at_ptr (pf | p) + val (pf3 | ()) = inc_at_ptr (pf2 | p) + val (pf4 | ()) = inc_at_ptr (pf3 | p) + in (pf4 | ()) end + +// 그래서 증명을 소비하지 않을 때 특별한 구문 설탕이 있습니다. +fn dec_at_ptr {l:addr} (pf: !int@l | p: ptr l): void = + !p := !p - 1 // ^ 느낌표를 주목하십시오. + +fn dec_three_times {l:addr} (pf: !int@l | p: ptr l): void = + let + val () = dec_at_ptr (pf | p) + val () = dec_at_ptr (pf | p) + val () = dec_at_ptr (pf | p) + in () end + +// dataview는 dataprop과 같지만 선형입니다. +// 주소가 null이거나 값이 있다는 증명입니다. +dataview MaybeNull(a:t@ype, addr) = + | NullPtr(a, null) + | {l:addr | l > null} NonNullPtr(a, l) of (a @ l) + +fn maybe_inc {l:addr} (pf: !MaybeNull(int, l) | p: ptr l): void = + if ptr1_is_null p + then () + else let + // a @ l의 증명에 접근하기 위해 증명을 해체합니다. + prval NonNullPtr(value_exists) = pf + val () = !p := !p + 1 + // 호출자를 위해 다시 재구성합니다. + prval () = pf := NonNullPtr(value_exists) + in () end + +// array_v(a,l,n)은 위치 l에 있는 n개의 a로 구성된 배열을 나타냅니다. +// 이것은 모든 증명이 삭제되므로 효율적인 for 루프로 컴파일됩니다. +fn sum_array {l:addr}{n:nat} (pf: !array_v(int,l,n) | p: ptr l, n: int n): int = + let + fun loop {l:addr}{n:nat} .. ( + pf: !array_v(int,l,n) + | ptr: ptr l, + length: int n, + acc: int + ): int = if length = 0 + then acc + else let + prval (head, rest) = array_v_uncons(pf) + val result = loop(rest | ptr_add(ptr, 1), length - 1, acc + !ptr) + prval () = pf := array_v_cons(head, rest) + in result end + in loop (pf | p, n, 0) end + +// 'var'는 스택 할당(lvalue) 변수를 만드는 데 사용됩니다. +val seven: int = let + var res: int = 3 + // 수정할 수 있습니다. + val () = res := res + 1 + // addr@ res는 이에 대한 포인터이고, view@ res는 관련 증명입니다. + val (pf | ()) = inc_three_times(view@ res | addr@ res) + // 변수가 범위를 벗어나기 전에 뷰를 반환해야 합니다. + prval () = view@ res := pf + in res end + +// 참조를 사용하면 C++에서와 같이 lvalue를 전달할 수 있습니다. +fn square (x: &int): void = + x := x * x // 수정할 수 있습니다. + +val sixteen: int = let + var res: int = 4 + val () = square res + in res end + +fn inc_at_ref (x: &int): void = + let + // var와 마찬가지로 참조에는 뷰와 주소가 있습니다. + val (pf | ()) = inc_at_ptr(view@ x | addr@ x) + prval () = view@ x := pf + in () end + +// 뷰에 대한 !와 마찬가지로 & 참조는 인수 타입으로만 합법적입니다. +// fn bad (x: &int): &int = x // 컴파일되지 않음 + +// 이것은 증명 int n @ l을 취하지만 증명 int (n+1) @ l을 반환합니다. +// 다른 타입이므로 이전처럼 !int @ l을 사용할 수 없습니다. +fn refined_inc_at_ptr {n:int}{l:addr} ( + pf: int n @ l | p: ptr l +): (int (n+1) @ l | void) = + let + val () = !p := !p + 1 + in (pf | ()) end + +// 다른 타입으로 증명을 반환하기 위한 특별한 구문 설탕 +fn refined_dec_at_ptr {n:int}{l:addr} ( + pf: !int n @ l >> int (n-1) @ l | p: ptr l +): void = + !p := !p - 1 + +// 합법적이지만 매우 나쁜 코드 +prfn swap_proofs {v1,v2:view} (a: !v1 >> v2, b: !v2 >> v1): void = + let + prval tmp = a + prval () = a := b + prval () = b := tmp + in () end + +// 참조에서도 작동합니다. +fn refined_square {n:int} (x: &int n >> int (n*n)): void = + x := x * x + +fn replace {a,b:vtype} (dest: &a >> b, src: b): a = + let + val old = dest + val () = dest := src + in old end + +// 값은 초기화되지 않을 수 있습니다. +fn {a:vt@ype} write (place: &a? >> a, value: a): void = + place := value + +val forty: int = let + var res: int + val () = write (res, 40) + in res end + +// viewtype: 뷰와 타입 +viewtypedef MaybeNullPtr(a:t@ype) = [l:addr] (MaybeNull(a, l) | ptr l) +// MaybeNullPtr은 타입 'viewtype'(일명 'vtype')을 가집니다. +// 타입은 vtype의 하위 타입이고 t@ype는 vt@ype의 하위 타입입니다. + +// 가장 일반적인 항등 함수: +fn {a:vt@ype} id7 (x: a) = x + +// 뷰를 포함하므로 viewtype은 선형으로 사용해야 합니다. +// fn {a:vt@ype} duplicate (x: a) = (x, x) // 컴파일되지 않음 +// fn {a:vt@ype} ignore (x: a) = () // 컴파일되지 않음 + +// arrayptr(a,l,n)은 편리한 내장 viewtype입니다. +fn easier_sum_array {l:addr}{n:nat} (p: !arrayptr(int,l,n), n: int n): int = + let + fun loop {i:nat | i <= n} ( + p: !arrayptr(int,l,n), n: int n, i: int i, acc: int + ): int = + if i = n + then acc + else loop(p, n, i+1, acc + p[i]) + in loop(p, n, 0, 0) end + + +/*************** 5부: dataviewtypes ****************/ + +// dataviewtype은 힙 할당 비공유 귀납적 타입입니다. + +// stdlib에서: +// dataviewtype list_vt(a:vt@ype, int) = +// | list_vt_nil(a, 0) +// | {n:nat} list_vt_cons(a, n+1) of (a, list_vt(a, n)) + +fn {a:vt@ype} length {n:int} (l: !list_vt(a, n)): int n = + let // ^ 소비하지 않으므로 + fun loop {acc:int} (l: !list_vt(a, n-acc), acc: int acc): int n = + case l of + | list_vt_nil() => acc + | list_vt_cons(head, tail) => loop(tail, acc + 1) + in loop (l, 0) end + +// vvvvv vt@ype가 아닙니다. vt@ype를 쉽게 제거할 수 없기 때문입니다. +fun {a:t@ype} destroy_list {n:nat} (l: list_vt(a,n)): void = + case l of + // ~ 패턴 매치는 해당 노드를 소비하고 해제합니다. + | ~list_vt_nil() => () + | ~list_vt_cons(_, tail) => destroy_list tail + +// 데이터 타입과 달리 dataviewtype은 수정할 수 있습니다: +fun {a:vt@ype} push_back {n:nat} ( + x: a, + l: &list_vt(a,n) >> list_vt(a,n+1) +): void = + case l of + | ~list_vt_nil() => l := list_vt_cons(x, list_vt_nil) + // @ 패턴 매치는 datavtype의 뷰를 분해/"펼치"므로 + // 구성 요소를 수정할 수 있습니다. + | @list_vt_cons(head, tail) => let + val () = push_back (x, tail) + // fold@로 다시 조립합니다. + prval () = fold@ l + in () end + +fun {a:vt@ype} pop_last {n:pos} (l: &list_vt(a,n) >> list_vt(a,n-1)): a = + let + val+ @list_vt_cons(head, tail) = l + in case tail of + | list_vt_cons _ => let + val res = pop_last tail + prval () = fold@ l + in res end + | ~list_vt_nil() => let + val head = head + // free@로 빈 datavtype 노드를 할당 해제합니다. + val () = free@{..}{0} l + val () = l := list_vt_nil() + in head end + /** 동등하게: + * | ~list_vt_nil() => let + * prval () = fold@ l + * val+ ~list_vt_cons(head, ~list_vt_nil()) = l + * val () = l := list_vt_nil() + * in head end + */ + end + +// "구멍"(즉, 초기화되지 않은 메모리)은 RHS에 _를 사용하여 만들 수 있습니다. +// 이 함수는 단일 꼬리 재귀 패스에서 목록을 복사하기 위해 대상 전달 스타일을 사용합니다. +fn {a:t@ype} copy_list {n:nat} (l: !list_vt(a, n)): list_vt(a, n) = + let + var res: ptr + fun loop {k:nat} (l: !list_vt(a, k), hole: &ptr? >> list_vt(a, k)): void = + case l of + | list_vt_nil() => hole := list_vt_nil + | list_vt_cons(first, rest) => let + val () = hole := list_vt_cons{..}{k-1}(first, _) + // ^ RHS: 구멍 + val+list_vt_cons(_, new_hole) = hole + // ^ LHS: 와일드카드 패턴(구멍 아님) + val () = loop (rest, new_hole) + prval () = fold@ hole + in () end + val () = loop (l, res) + in res end + +// 연결 리스트를 *제자리에서* 뒤집습니다 -- 할당이나 해제가 없습니다. +fn {a:vt@ype} in_place_reverse {n:nat} (l: list_vt(a, n)): list_vt(a, n) = + let + fun loop {k:nat} (l: list_vt(a, n-k), acc: list_vt(a, k)): list_vt(a, n) = + case l of + | ~list_vt_nil() => acc + | @list_vt_cons(x, tail) => let + val rest = replace(tail, acc) + // 노드 'l'은 이제 원래 목록 대신 acc의 일부입니다. + prval () = fold@ l + in loop (rest, l) end + in loop (l, list_vt_nil) end + + +/*************** 6부: 기타 추가 사항 ****************/ + +// 레코드 +// Point는 타입 't@ype'을 가집니다. +typedef Point = @{ x= int, y= int } +val origin: Point = @{ x= 0, y= 0 } + +// 튜플과 레코드는 일반적으로 언박싱되지만, 박싱된 변형이 있습니다. +// BoxedPoint는 타입 'type'을 가집니다. +typedef BoxedPoint = '{ x= int, y= int } +val boxed_pair: '(int,int) = '(5, 3) + +// 함수에 단일 인수로 쌍을 전달할 때, 다중 인수 함수와의 모호성을 피하기 위해 +// @(a,b)로 작성해야 합니다. +val six_plus_seven = let + fun sum_of_pair (pair: (int,int)) = pair.0 + pair.1 + in sum_of_pair @(6, 7) end + +// 생성자에 관련 데이터가 없는 경우(예: None()), 표현식에서 괄호는 +// 선택 사항입니다. 그러나 패턴에서는 필수입니다. +fn inc_option (opt: Option int) = + case opt of + | Some(x) => Some(x+1) + | None() => None + +// ATS는 간단한 FFI를 가지고 있습니다. C로 컴파일되고 (대부분) C ABI를 사용하기 때문입니다. +%{ +// 인라인 C 코드 +int scanf_wrapper(void *format, void *value) { + return scanf((char *) format, (int *) value); +} +%} +// 원한다면 scanf 결과에 대해 더 정확하게 설명하는 사용자 정의 dataviewtype을 정의할 수 있습니다. +extern fn scanf (format: string, value: &int): int = "scanf_wrapper" + +fn get_input_number (): Option int = + let + var x: int = 0 + in + if scanf("%d\n", x) = 1 + then Some(x) + else None + end + +// extern은 별도의 선언 및 정의에도 사용됩니다. +extern fn say_hi (): void +// 나중에 또는 다른 파일에서: +implement say_hi () = print "hi\n" + +// main0을 구현하면 주 함수로 실행됩니다. +// implmnt는 implement의 별칭입니다. +implmnt main0 () = () + +// 공리에 대해서도 마찬가지입니다: +extern praxi view_id {a:view} (x: a): a +// 실제로 공리를 구현할 필요는 없지만, 할 수 있습니다. +primplmnt view_id x = x +// primplmnt는 primplement의 별칭입니다. + +// 일부 표준 별칭은 다음과 같습니다: +// List0(a) = [n:nat] list(a,n) 및 List0_vt(a) = [n:nat] list_vt(a,n) +// t0p = t@ype 및 vt0p = vt@ype +fun {a:t0p} append (xs: List0 a, ys: List0 a): List0 a = + case xs of + | list_nil() => ys + | list_cons(x, xs) => list_cons(x, append(xs, ys)) + +// 차례로 작업을 수행하는 방법은 여러 가지가 있습니다. +val let_in_example = let + val () = print "thing one\n" + val () = print "thing two\n" + in () end + +val parens_example = (print "thing one\n"; print "thing two\n") + +val begin_end_example = begin + print "thing one\n"; + print "thing two\n"; // 선택적 후행 세미콜론 + end + +// 지역 변수를 사용하는 방법도 여러 가지가 있습니다. +fun times_four_let x = + let + fun times_two (x: int) = x * 2 + in times_two (times_two x) end + +local + fun times_two (x: int) = x * 2 +in + fun times_four_local x = times_two (times_two x) +end + +fun times_four_where x = times_two (times_two x) + where { + fun times_two (x: int) = x * 2 + } + +//// ATS의 마지막 주석 종류는 파일 끝 주석입니다. + +네 개의 슬래시와 파일 끝 사이에 있는 모든 것은 무시됩니다. + +ATS를 즐기세요! +``` + +## 더 알아보기 + +이것이 ATS의 전부는 아닙니다 -- 특히 클로저 및 효과 시스템과 같은 일부 핵심 기능은 생략되었으며, 모듈 및 빌드 시스템과 같은 덜 타입적인 내용도 생략되었습니다. 이 섹션을 직접 작성하고 싶다면 기여를 환영합니다! + +ATS에 대해 더 자세히 알아보려면 [ATS 웹사이트](http://www.ats-lang.org/), 특히 [문서 페이지](http://www.ats-lang.org/Documents.html)를 방문하십시오. + +``` \ No newline at end of file diff --git a/ko/awk.md b/ko/awk.md new file mode 100644 index 0000000000..10d2d85e7c --- /dev/null +++ b/ko/awk.md @@ -0,0 +1,335 @@ +--- +category: tool +name: AWK +filename: learnawk.awk +contributors: + - ["Marshall Mason", "http://github.com/marshallmason"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +AWK는 모든 POSIX 호환 UNIX 시스템의 표준 도구입니다. 명령줄에서 사용하는 flex/lex와 같으며, 텍스트 처리 작업 및 기타 스크립팅 요구에 적합합니다. C와 유사한 구문을 가지고 있지만, 필수 세미콜론(단, 한 줄짜리 스크립트를 작성할 때는 필요하며, AWK는 이 점에서 뛰어납니다), 수동 메모리 관리 또는 정적 타이핑이 없습니다. 텍스트 처리에 탁월합니다. 셸 스크립트에서 호출하거나 독립 실행형 스크립팅 언어로 사용할 수 있습니다. + +Perl 대신 AWK를 사용하는 이유는 무엇일까요? 가독성입니다. AWK는 Perl보다 읽기 쉽습니다. 간단한 텍스트 처리 스크립트, 특히 파일을 한 줄씩 읽고 구분 기호로 분할하는 스크립트의 경우 AWK가 적합한 도구일 것입니다. + +```awk +#!/usr/bin/awk -f + +# 주석은 이렇습니다 + + +# AWK 프로그램은 패턴과 액션의 모음으로 구성됩니다. +pattern1 { action; } # lex와 같습니다 +pattern2 { action; } # + +# 암시적인 루프가 있으며 AWK는 제공된 각 파일의 각 레코드를 자동으로 읽고 구문 분석합니다. 각 레코드는 기본적으로 공백(여러 공백, 탭은 하나로 계산됨)인 FS 구분 기호로 분할됩니다. +# 명령줄(-F C) 또는 BEGIN 패턴에서 FS를 할당할 수 있습니다. + +# 특수 패턴 중 하나는 BEGIN입니다. BEGIN 패턴은 파일을 읽기 전에 참입니다. END 패턴은 마지막 파일(또는 파일이 지정되지 않은 경우 표준 입력)의 파일 끝 이후에 참입니다. +# 또한 할당할 수 있는 출력 필드 구분 기호(OFS)가 있으며, 기본값은 단일 공백입니다. + +BEGIN { + + # BEGIN은 프로그램 시작 시 실행됩니다. 텍스트 파일을 처리하기 전에 모든 예비 설정 코드를 넣는 곳입니다. 텍스트 파일이 없는 경우 BEGIN을 기본 진입점으로 생각하십시오. + + # 변수는 전역입니다. 선언할 필요 없이 설정하거나 사용하십시오. + count = 0; + + # C 및 친구들과 같은 연산자 + a = count + 1; + b = count - 1; + c = count * 1; + d = count / 1; # 정수 나눗셈 + e = count % 1; # 나머지 + f = count ^ 1; # 거듭제곱 + + a += 1; + b -= 1; + c *= 1; + d /= 1; + e %= 1; + f ^= 1; + + # 1씩 증가 및 감소 + a++; + b--; + + # 접두사 연산자로 사용하면 증가된 값을 반환합니다. + ++a; + --b; + + # 또한 문장을 끝내는 세미콜론과 같은 구두점이 없습니다. + + # 제어문 + if (count == 0) + print "Starting with count of 0"; + else + print "Huh?"; + + # 또는 삼항 연산자를 사용할 수 있습니다. + print (count == 0) ? "Starting with count of 0" : "Huh?"; + + # 여러 줄로 구성된 블록은 중괄호를 사용합니다. + while (a < 10) { + print "String concatenation is done" " with a series" " of" + " space-separated strings"; + print a; + + a++; + } + + for (i = 0; i < 10; i++) + print "Good ol' for loop"; + + # 비교에 관해서는 표준입니다: + # a < b # 미만 + # a <= b # 이하 + # a != b # 같지 않음 + # a == b # 같음 + # a > b # 초과 + # a >= b # 이상 + + # 논리 연산자도 있습니다. + # a && b # AND + # a || b # OR + + # 또한 매우 유용한 정규식 일치가 있습니다. + if ("foo" ~ "^fo+$") + print "Fooey!"; + if ("boo" !~ "^fo+$") + print "Boo!"; + + # 배열 + arr[0] = "foo"; + arr[1] = "bar"; + + # 내장 함수 split()으로 배열을 초기화할 수도 있습니다. + + n = split("foo:bar:baz", arr, ":"); + + # 연관 배열도 있습니다(실제로 모두 연관 배열입니다). + assoc["foo"] = "bar"; + assoc["bar"] = "baz"; + + # 그리고 다차원 배열도 있습니다. 여기서는 언급하지 않을 몇 가지 제한 사항이 있습니다. + multidim[0,0] = "foo"; + multidim[0,1] = "bar"; + multidim[1,0] = "baz"; + multidim[1,1] = "boo"; + + # 배열 멤버십을 테스트할 수 있습니다. + if ("foo" in assoc) + print "Fooey!"; + + # 'in' 연산자를 사용하여 배열의 키를 순회할 수도 있습니다. + for (key in assoc) + print assoc[key]; + + # 명령줄은 ARGV라는 특수 배열에 있습니다. + for (argnum in ARGV) + print ARGV[argnum]; + + # 배열의 요소를 제거할 수 있습니다. + # 이것은 AWK가 인수를 처리할 파일로 가정하는 것을 방지하는 데 특히 유용합니다. + delete ARGV[1]; + + # 명령줄 인수 수는 ARGC라는 변수에 있습니다. + print ARGC; + + # AWK에는 여러 내장 함수가 있습니다. 세 가지 범주로 나뉩니다. 나중에 정의된 자체 함수에서 각각을 시연하겠습니다. + + return_value = arithmetic_functions(a, b, c); + string_functions(); + io_functions(); +} + +# 함수를 정의하는 방법은 다음과 같습니다. +function arithmetic_functions(a, b, c, d) { + + # 아마도 AWK에서 가장 성가신 부분은 지역 변수가 없다는 것입니다. + # 모든 것이 전역입니다. 짧은 스크립트의 경우 이것은 괜찮고 유용하기까지 하지만, 긴 스크립트의 경우 문제가 될 수 있습니다. + + # 해결 방법(해킹)이 있습니다. 함수 인수는 함수에 로컬이며, AWK는 필요한 것보다 더 많은 함수 인수를 정의할 수 있습니다. 따라서 위에서 했던 것처럼 함수 선언에 지역 변수를 넣으십시오. 관례적으로 실제 함수 매개변수와 지역 변수를 구별하기 위해 약간의 추가 공백을 넣으십시오. 이 예에서 a, b, c는 실제 매개변수이고 d는 단지 지역 변수입니다. + + # 이제 산술 함수를 시연하겠습니다. + + # 대부분의 AWK 구현에는 몇 가지 표준 삼각 함수가 있습니다. + d = sin(a); + d = cos(a); + d = atan2(b, a); # b / a의 아크탄젠트 + + # 그리고 로그 관련 항목 + d = exp(a); + d = log(a); + + # 제곱근 + d = sqrt(a); + + # 부동 소수점을 정수로 자르기 + d = int(5.34); # d => 5 + + # 난수 + srand(); # 인수로 시드를 제공합니다. 기본적으로 현재 시간을 사용합니다. + d = rand(); # 0과 1 사이의 난수. + + # 값을 반환하는 방법은 다음과 같습니다. + return d; +} + +function string_functions( localvar, arr) { + + # AWK는 문자열 처리 언어이므로 여러 문자열 관련 함수가 있으며, 그 중 다수는 정규식에 크게 의존합니다. + + # 검색 및 바꾸기, 첫 번째 인스턴스(sub) 또는 모든 인스턴스(gsub) + # 둘 다 바뀐 일치 항목 수를 반환합니다. + localvar = "fooooobar"; + sub("fo+", "Meet me at the ", localvar); # localvar => "Meet me at the bar" + gsub("e", ".", localvar); # localvar => "M..t m. at th. bar" + + # 정규식과 일치하는 문자열 검색 + # index()는 동일한 작업을 수행하지만 정규식을 허용하지 않습니다. + match(localvar, "t"); # => 4, 't'가 네 번째 문자이므로 + + # 구분 기호로 분할 + n = split("foo-bar-baz", arr, "-"); + # 결과: a[1] = "foo"; a[2] = "bar"; a[3] = "baz"; n = 3 + + # 기타 유용한 항목 + sprintf("%s %d %d %d", "Testing", 1, 2, 3); # => "Testing 1 2 3" + substr("foobar", 2, 3); # => "oob" + substr("foobar", 4); # => "bar" + length("foo"); # => 3 + tolower("FOO"); # => "foo" + toupper("foo"); # => "FOO" +} + +function io_functions( localvar) { + + # 이미 print를 보셨습니다. + print "Hello world"; + + # printf도 있습니다. + printf("%s %d %d %d\n", "Testing", 1, 2, 3); + + # AWK에는 파일 핸들이 없습니다. 파일 핸들이 필요한 것을 사용할 때 자동으로 파일 핸들을 엽니다. 이를 위해 사용한 문자열은 I/O 목적으로 파일 핸들로 처리될 수 있습니다. 이것은 셸 스크립팅과 비슷하게 느껴지지만, 동일한 출력을 얻으려면 문자열이 정확히 일치해야 하므로 변수를 사용하십시오: + + outfile = "/tmp/foobar.txt"; + + print "foobar" > outfile; + + # 이제 문자열 outfile은 파일 핸들입니다. 닫을 수 있습니다: + close(outfile); + + # 셸에서 무언가를 실행하는 방법은 다음과 같습니다. + system("echo foobar"); # => foobar 인쇄 + + # 표준 입력에서 한 줄을 읽고 localvar에 저장합니다. + getline localvar; + + # 파이프에서 한 줄을 읽습니다(다시 말하지만, 제대로 닫으려면 문자열을 사용하십시오). + cmd = "echo foobar"; + cmd | getline localvar; # localvar => "foobar" + close(cmd); + + # 파일에서 한 줄을 읽고 localvar에 저장합니다. + infile = "/tmp/foobar.txt"; + getline localvar < infile; + close(infile); +} + +# 처음에 말했듯이 AWK 프로그램은 패턴과 액션의 모음으로 구성됩니다. 이미 BEGIN 패턴을 보셨습니다. 다른 패턴은 파일이나 표준 입력에서 줄을 처리하는 경우에만 사용됩니다. +# +# AWK에 인수를 전달하면 처리할 파일 이름으로 처리됩니다. +# 순서대로 모두 처리합니다. 암시적인 for 루프처럼 생각하십시오. +# 이러한 파일의 줄을 반복합니다. 이러한 패턴과 액션은 루프 내부의 스위치 문과 같습니다. + +/^fo+bar$/ { + + # 이 액션은 정규식 /^fo+bar$/와 일치하는 모든 줄에 대해 실행되고, 일치하지 않는 줄은 건너뜁니다. 줄을 인쇄해 보겠습니다: + + print; + + # 인수가 없습니다! print에는 기본 인수인 $0이 있기 때문입니다. + # $0은 현재 처리 중인 줄의 이름입니다. 자동으로 생성됩니다. + + # 다른 $ 변수가 있다고 추측할 수 있습니다. 모든 줄은 모든 액션이 호출되기 전에 암시적으로 분할됩니다. 셸과 마찬가지로 말이죠. 그리고 셸과 마찬가지로 각 필드는 달러 기호로 접근할 수 있습니다. + + # 이것은 줄의 두 번째와 네 번째 필드를 인쇄합니다. + print $2, $4; + + # AWK는 각 줄을 검사하고 처리하는 데 도움이 되는 많은 다른 변수를 자동으로 정의합니다. 가장 중요한 것은 NF입니다. + + # 이 줄의 필드 수를 인쇄합니다. + print NF; + + # 이 줄의 마지막 필드를 인쇄합니다. + print $NF; +} + +# 모든 패턴은 실제로 참/거짓 테스트입니다. 마지막 패턴의 정규식도 참/거짓 테스트이지만, 일부는 숨겨져 있습니다. 테스트할 문자열을 제공하지 않으면 현재 처리 중인 줄인 $0을 가정합니다. 따라서 완전한 버전은 다음과 같습니다: + +$0 ~ /^fo+bar$/ { + print "Equivalent to the last pattern"; +} + +a > 0 { + # 이것은 a가 양수인 한 각 줄에 대해 한 번 실행됩니다. +} + +# 이제 이해하셨을 겁니다. 텍스트 파일을 처리하고, 한 번에 한 줄씩 읽고, 그것으로 무언가를 하는 것, 특히 구분 기호로 분할하는 것은 UNIX에서 매우 일반적이므로 AWK는 묻지 않고도 모든 것을 수행하는 스크립팅 언어입니다. 입력에 대해 예상하는 것과 그것으로 무엇을 하고 싶은지에 따라 패턴과 액션을 작성하기만 하면 됩니다. + +# 다음은 간단한 스크립트의 빠른 예입니다. AWK가 완벽하게 적합한 종류의 것입니다. 표준 입력에서 이름을 읽은 다음 해당 이름의 모든 사람의 평균 연령을 인쇄합니다. 이 데이터 파일의 이름을 인수로 제공한다고 가정해 보겠습니다: +# +# Bob Jones 32 +# Jane Doe 22 +# Steve Stevens 83 +# Bob Smith 29 +# Bob Barker 72 +# +# 스크립트는 다음과 같습니다: + +BEGIN { + + # 먼저 사용자에게 이름을 묻습니다. + print "What name would you like the average age for?"; + + # 명령줄의 파일이 아닌 표준 입력에서 한 줄을 가져옵니다. + getline name < "/dev/stdin"; +} + +# 이제 첫 번째 필드가 주어진 이름인 모든 줄을 일치시킵니다. +$1 == name { + + # 여기서는 이미 미리 로드된 여러 유용한 변수에 접근할 수 있습니다: + # $0은 전체 줄입니다. + # $3은 세 번째 필드, 즉 우리가 관심 있는 나이입니다. + # NF는 필드 수이며, 3이어야 합니다. + # NR은 지금까지 본 레코드(줄) 수입니다. + # FILENAME은 처리 중인 파일의 이름입니다. + # FS는 사용 중인 필드 구분 기호이며, 여기서는 " "입니다. + # ...등등. 맨 페이지에 문서화된 더 많은 것이 있습니다. + + # 실행 중인 총계와 일치하는 줄 수를 추적합니다. + sum += $3; + nlines++; +} + +# 또 다른 특수 패턴은 END라고 합니다. 모든 텍스트 파일을 처리한 후에 실행됩니다. BEGIN과 달리 입력을 처리하도록 지정한 경우에만 실행됩니다. 모든 파일이 제공된 규칙 및 액션에 따라 읽고 처리된 후에 실행됩니다. 그 목적은 일반적으로 일종의 최종 보고서를 출력하거나 스크립트 과정에서 축적한 데이터의 집계로 무언가를 하는 것입니다. + +END { + if (nlines) + print "The average age for " name " is " sum / nlines; +} +``` + +더 읽을거리: + +* [Awk 튜토리얼](http://www.grymoire.com/Unix/Awk.html) +* [Awk 맨 페이지](https://linux.die.net/man/1/awk) +* [GNU Awk 사용자 가이드](https://www.gnu.org/software/gawk/manual/gawk.html) + GNU Awk는 대부분의 Linux 시스템에서 찾을 수 있습니다. +* [AWK 한 줄 모음](http://tuxgraphics.org/~guido/scripts/awk-one-liner.html) +* [Awk alpinelinux 위키](https://wiki.alpinelinux.org/wiki/Awk) 기술적 + 요약 및 "함정" 목록(다른 구현이 + 다르거나 예기치 않은 방식으로 동작할 수 있는 곳). +* [awk를 위한 기본 라이브러리](https://github.com/dubiousjim/awkenough) \ No newline at end of file diff --git a/ko/ballerina.md b/ko/ballerina.md new file mode 100644 index 0000000000..14288b6815 --- /dev/null +++ b/ko/ballerina.md @@ -0,0 +1,416 @@ +--- +name: Ballerina +contributors: + - ["Anjana Fernando", "https://github.com/lafernando"] +filename: learn_ballerina.bal +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +[Ballerina](https://ballerina.io/)는 클라우드 개발을 즐거운 경험으로 만들기 위한 정적 타입 프로그래밍 언어입니다. + +```java +// 한 줄 주석 + +// 현재 소스 파일로 모듈 가져오기 +import ballerina/io; +import ballerina/time; +import ballerina/http; +import ballerinax/java.jdbc; +import ballerina/lang.'int as ints; +import ballerinax/awslambda; +// 모듈 별칭 "af"는 전체 모듈 이름 대신 코드에서 사용됩니다. +import ballerinax/azure.functions as af; + +http:Client clientEP = new ("https://freegeoip.app/"); +jdbc:Client accountsDB = new ({url: "jdbc:mysql://localhost:3306/AccountsDB", + username: "test", password: "test"}); + +// 서비스는 Ballerina에서 일급 개념이며 Ballerina 프로그램의 진입점 중 하나입니다. +// Ballerina 플랫폼은 또한 Kubernetes와 같은 환경에 쉽게 배포할 수 있도록 지원합니다(https://ballerina.io/learn/deployment/kubernetes/). +service geoservice on new http:Listener(8080) { + + @http:ResourceConfig { + path: "/geoip/{ip}" + } + resource function geoip(http:Caller caller, http:Request request, + string ip) returns @tainted error? { + http:Response resp = check clientEP->get("/json/" + <@untainted>ip); + check caller->respond(<@untainted> check resp.getTextPayload()); + } + +} + +// AWS Lambda를 사용한 서버리스 함수형 서비스 지원. +// Ballerina 컴파일러는 배포할 최종 배포 아티팩트를 자동으로 생성합니다. +@awslambda:Function +public function echo(awslambda:Context ctx, json input) returns json { + return input; +} + +@awslambda:Function +public function notifyS3(awslambda:Context ctx, + awslambda:S3Event event) returns json { + return event.Records[0].s3.'object.key; +} + +// Azure Functions를 사용한 서버리스 함수형 서비스 지원. +// AWS Lambda와 유사하게 컴파일러는 배포 아티팩트를 생성합니다. +@af:Function +public function fromQueueToQueue(af:Context ctx, + @af:QueueTrigger { queueName: "queue1" } string inMsg, + @af:QueueOutput { queueName: "queue2" } af:StringOutputBinding outMsg) { + outMsg.value = inMsg; +} + +// 사용자 정의 레코드 유형 +public type Person record { + string id; // 필수 필드 + string name; + int age?; // 선택적 필드 + string country = "N/A"; // 기본값 +}; + +@af:Function +public function fromHttpTriggerCosmosDBInput( + @af:HTTPTrigger { route: "c1/{country}" } af:HTTPRequest httpReq, + @af:CosmosDBInput { connectionStringSetting: "CosmosDBConnection", + databaseName: "db1", collectionName: "c1", + sqlQuery: "select * from c1 where c1.country = {country}" } + Person[] dbReq) + returns @af:HTTPOutput string|error { + return dbReq.toString(); +} + +public function main() returns @tainted error? { + int a = 10; // 64비트 부호 있는 정수 + float b = 1.56; // 64비트 IEEE 754-2008 이진 부동 소수점 수 + string c = "hello"; // 유니코드 문자열 + boolean d = true; // true, false + decimal e = 15.335; // 십진 부동 소수점 수 + + var f = 20; // 'var'를 사용한 유형 추론 - 'f'는 int입니다. + + int[] intArray = [1, 2, 3, 4, 5, 6]; + int x = intArray.shift(); // 디큐 작업과 유사 + x = intArray.pop(); // 마지막 요소 제거 + intArray.push(10); // 끝에 추가 + + // 튜플 - 각 슬롯에 대해 고유한 유형을 가진 고정 길이 배열과 유사 + [string, int] p1 = ["Jack", 1990]; + [string, int] p2 = ["Tom", 1986]; + io:println("Name: ", p1[0], " Birth Year: ", p1[1]); + + string name1; + int birthYear1; + [name1, birthYear1] = p1; // 튜플 구조 분해 + + var [name2, birthYear2] = p2; // 동일한 문에서 값 선언 및 할당 + + // If 문 + int ix = 10; + if ix < 10 { + io:println("value is less than 10"); + } else if ix == 10 { + io:println("value equals to 10"); + } else { + io:println("value is greater than 10"); + } + + // 루프 + int count = 10; + int i = 0; + while i < 10 { + io:println(i); + } + // 0에서 count까지 루프(포함) + foreach var j in 0...count { + io:println(j); + } + // 0에서 count까지 루프(미포함) + foreach var j in 0..{{name1}}{{birthYear1}}`; + io:println(x1); + // XML 값의 특정 요소에 액세스 + io:println(x1/); + // XML 값의 모든 자식 항목 나열 + io:println(x1/*); + + // 함수 호출 + x = add(1, 2); + io:println(multiply(2, 4)); + // 기본 매개변수에 대한 값을 제공하는 호출 + io:println(multiply(3, 4, true)); + // 나머지 매개변수(다중 값)에 대한 값을 사용한 호출 + io:println(addAll(1, 2, 3)); + io:println(addAll(1, 2, 3, 4, 5)); + + // 함수 포인터 + (function (int, int) returns int) op1 = getOperation("add"); + (function (int, int) returns int) op2 = getOperation("mod"); + io:println(op1(5, 10)); + io:println(op2(13, 10)); + + // 클로저 + (function (int x) returns int) add5 = getAdder(5); + (function (int x) returns int) add10 = getAdder(10); + io:println(add5(10)); + io:println(add10(10)); + + int[] numbers = [1, 2, 3, 4, 5, 6, 7, 8]; + // 함수형 반복 + int[] evenNumbers = numbers.filter(function (int x) returns boolean { return x % 2 == 0; }); + + // 유니온 유형 - "input"은 문자열 또는 byte[] 유형입니다. + string|byte[] uval = "XXX"; + + // 유형 테스트 표현식("uval is string")을 사용하여 변수의 런타임 유형을 확인할 수 있습니다. + if uval is string { + // 현재 범위에서 "uval"은 문자열 값입니다. + string data = "data:" + uval; + } else { + // "if" 문의 표현식에서 문자열이 아님을 배제했으므로 남은 유일한 유형은 "byte[]"입니다. 따라서 현재 범위에서 "uval"은 항상 "byte[]"입니다. + int inputLength = uval.length(); + } + + // 오류 처리 + string input = io:readln("Enter number: "); + int|error result = ints:fromString(input); + if result is int { + io:println("Number: ", result); + } else { + io:println("Invalid number: ", input); + } + + // check 표현식을 사용하여 하위 표현식이 런타임에 오류 값으로 평가된 경우 현재 함수에서 오류를 직접 반환할 수 있습니다. + int number = check ints:fromString(input); + + // 함수에서 워커를 사용한 동시 실행 + doWorkers(); + + // 미래를 사용한 비동기 실행 + future f10 = start fib(10); + var webresult = clientEP->get("/"); + int fresult = wait f10; + if webresult is http:Response { + io:println(webresult.getTextPayload()); + io:println(fresult); + } + + // 매핑 유형 + map ageMap = {}; + ageMap["Peter"] = 25; + ageMap["John"] = 30; + + int? agePeter = ageMap["Peter"]; // int?는 유니온 유형 int|() - int 또는 nill입니다. + if agePeter is int { + io:println("Peter's age is ", agePeter); + } else { + io:println("Peter's age is not found"); + } + + Person person1 = { id: "p1", name : "Anne", age: 28, country: "Sri Lanka" }; + Scores score1 = { physics : 80, mathematics: 95 }; + score1["chemistry"] = 75; + io:println(score1["chemistry"]); + + Student student1 = { id: "s1", name: "Jack", age: 25, country: "Japan" }; + student1.college = "Stanford"; + string? jacksCollege = student1?.college; // 선택적 필드 액세스 + if jacksCollege is string { + io:println("Jack's college is ", jacksCollege); + } + + // 구조적 유형 시스템으로 인해 "student1"은 "person2"에 할당될 수 있습니다. + // student1의 구조가 person2의 구조와 호환되기 때문입니다. + // 여기서 "Student"는 "Person"이라고도 할 수 있습니다. + Person person2 = student1; + + map grades = {"Jack": 95, "Anne": 90, "John": 80, "Bill": 55}; + Person px1 = {id: "px1", name: "Jack", age: 30, country: "Canada"}; + Person px2 = {id: "px2", name: "John", age: 25}; + Person px3 = {id: "px3", name: "Anne", age: 17, country: "UK"}; + Person px4 = {id: "px4", name: "Bill", age: 15, country: "USA"}; + Person[] persons = []; + persons.push(px1); + persons.push(px2); + persons.push(px3); + persons.push(px4); + + // 목록 데이터에 대한 복잡한 쿼리를 실행하는 데 사용되는 쿼리 표현식 + Result[] results = from var person in persons + let int lgrade = (grades[person.name] ?: 0) + where lgrade > 75 + let string targetCollege = "Stanford" + select { + name: person.name, + college: targetCollege, + grade: lgrade + }; + + // 신뢰할 수 없는 데이터를 처리하기 위한 컴파일 타임 오염 검사 + string s1 = "abc"; + mySecureFunction(s1); + // "s2"를 명시적으로 오염된 값으로 만듭니다. Ballerina 프로그램에 대한 외부 입력(예: 명령줄 인수 및 네트워크 입력)은 기본적으로 오염된 데이터로 표시됩니다. + string s2 = <@tainted> s1; + // "s2x"는 이제 오염된 값입니다. 왜냐하면 그 값은 오염된 값(s1)을 사용하여 파생되었기 때문입니다. + string s2x = s2 + "abc"; + // 다음 줄의 주석을 해제하면 컴파일 오류가 발생합니다. 왜냐하면 오염되지 않은 값을 예상하는 함수에 오염된 값(s2x)을 전달하기 때문입니다. + // mySecureFunction(s2x); + + // 객체 인스턴스화 + Employee emp1 = new("E0001", "Jack Smith", "Sales", 2009); + io:println("The company service duration of ", emp1.name, + " is ", emp1.serviceDuration()); + + // 지원되는 작업은 "transaction" 블록에 작업을 묶어 트랜잭션에서 실행할 수 있습니다. + transaction { + // 아래 데이터베이스 작업을 단일 로컬 트랜잭션에서 실행합니다. + var r1 = accountsDB->update("UPDATE Employee SET balance = balance + ? WHERE id = ?", 5500.0, "ID001"); + var r2 = accountsDB->update("UPDATE Employee SET balance = balance + ? WHERE id = ?", 5500.0, "ID001"); + } +} + +// 객체는 데이터와 기능을 모두 캡슐화하는 행동 유형입니다. +type Employee object { + + // 개인 필드는 객체 및 해당 메서드 내에서만 볼 수 있습니다. + private string empId; + // 공용 필드는 누구나 액세스할 수 있습니다. + public string name; + public string department; + // 기본 한정자는 "보호된" 필드이며, 모듈 내에서만 액세스할 수 있습니다. + int yearJoined; + + // 객체 초기화 함수; 객체가 인스턴스화될 때 자동으로 호출됩니다. + public function __init(string empId, string name, string department, int yearJoined) { + self.empId = empId; + self.name = name; + self.department = department; + self.yearJoined = yearJoined; + } + + // 객체 메서드 + public function serviceDuration() returns int { + time:Time ct = time:currentTime(); + return time:getYear(ct) - self.yearJoined; + } + +}; + +// 학생은 사람의 하위 유형입니다. +type Student record { + string id; + string name; + int age; + string college?; + string country; +}; + +type Scores record { + int physics; + int mathematics; +}; + +type Result record { + string name; + string college; + int grade; +}; + +public function getOperation(string op) returns (function (int, int) returns int) { + if op == "add" { + return add; + } else if op == "mod" { + return function (int a, int b) returns int { // 익명 함수 + return a % b; + }; + } else { + return (x, y) => 0; // 단일 표현식 익명 no-op 함수 + } +} + +// 두 개의 필수 매개변수 +public function add(int a, int b) returns int { + return a + b; +} + +// 'log'는 기본 매개변수입니다. +public function multiply(int a, int b, boolean log = false) returns int { + if log { + io:println("Multiplying ", a, " with ", b); + } + return a * b; +} + +// 'numbers'는 나머지 매개변수입니다 - 배열과 유사하게 여러 값을 가질 수 있습니다. +public function addAll(int... numbers) returns int { + int result = 0; + foreach int number in numbers { + result += number; + } + return result; +} + +public function getAdder(int n) returns (function (int x) returns int) { + return function (int x) returns int { // 클로저 반환 + return x + n; + }; +} + +function fib(int n) returns int { + if n <= 2 { + return 1; + } else { + return fib(n - 1) + fib(n - 2); + } +} + +// 워커 블록 "w1" 및 "w2"의 코드는 이 함수가 호출될 때 동시에 실행됩니다. "wait" 표현식은 주어진 워커가 결과를 검색하기 위해 완료될 때까지 기다립니다. +public function doWorkers() { + worker w1 returns int { + int j = 10; + j -> w2; + int b; + b = <- w2; + return b * b; + } + worker w2 returns int { + int a; + a = <- w1; + a * 2 -> w1; + return a + 2; + } + record {int w1; int w2;} x = wait {w1, w2}; + io:println(x); +} + +// 오염되지 않은 문자열 값만 사용하는 함수입니다. +public function mySecureFunction(@untainted string input) { + io:println(input); +} +``` + +### 더 읽을거리 + +* [예제로 배우는 Ballerina](https://ballerina.io/learn/by-example/) +* [사용자 가이드](https://ballerina.io/learn/installing-ballerina/) +* [API 문서](https://ballerina.io/learn/api-docs/ballerina/) +* [언어 사양](https://ballerina.io/spec/) \ No newline at end of file diff --git a/ko/bash.md b/ko/bash.md index d0ee5ebdb7..a8e87e132e 100644 --- a/ko/bash.md +++ b/ko/bash.md @@ -9,7 +9,7 @@ contributors: - ["Rahil Momin", "https://github.com/iamrahil"] - ["Gregrory Kielian", "https://github.com/gskielian"] - ["Etan Reisner", "https://github.com/deryni"] - - ["Jonathan Wang", "https://github.com/Jonathansw"] + - ["Jonathan Wang", "https://github.com/Jonathansw"] - ["Leo Rudberg", "https://github.com/LOZORD"] - ["Betsy Lorton", "https://github.com/schbetsy"] - ["John Detter", "https://github.com/jdetter"] @@ -375,4 +375,4 @@ info bash info bash 'Bash Features' info bash 6 info --apropos bash -``` +``` \ No newline at end of file diff --git a/ko/bc.md b/ko/bc.md new file mode 100644 index 0000000000..b55ee20c97 --- /dev/null +++ b/ko/bc.md @@ -0,0 +1,99 @@ +--- +name: bc +contributors: + - ["Btup"] +filename: learnbc.bc +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```bc +/*이것은 여러 줄 +주석입니다.*/ +# 이것은 또한 (한 줄) 주석입니다! (GNU bc에서). + + /*1. 변수 및 제어 구조*/ +num = 45 /*모든 변수는 더블만 저장하며, + 문자열 상수를 직접 저장할 수 없습니다.*/ +num = 45; /*모든 문장 뒤에 세미콜론을 추가할 수 있습니다. + 이것은 선택 사항입니다.*/ +/*블록은 {} 연산자로 표시됩니다(C와 유사):*/ +while(num < 50) { + num += 1 /*num=num+1과 동일합니다. + a = a op b는 a op= b와 동일합니다.*/ +} +/*그리고 ++(증가) 및 --(감소) 연산자가 있습니다.*/ +/*세 가지 특수 변수가 있습니다: +scale: 더블 숫자의 스케일을 정의합니다. +ibase: 입력의 밑을 정의합니다. +obase: 출력의 밑을 정의합니다.*/ +/*If 절:*/ +hour = read() /*숫자 입력*/ + +if(hour < 12) { /*연산자는 C와 정확히 같습니다.*/ + print "Good morning\n" /*"print"는 쉼표로 구분된 문자열 또는 변수를 출력합니다.*/ +} else if(hour == 12) { + print "Hello\n" + /*이스케이프 시퀀스는 문자열에서 \로 시작합니다. + 이스케이프 시퀀스를 더 명확하게 하기 위해, 여기에 + bc에서 작동하는 간단한 목록이 있습니다: + \b: 백스페이스 + \c: 캐리지 리턴 + \n: 줄 바꿈 + \t: 탭 + \\: 백슬래시*/ +} else { + print "Good afternoon\n" +} + +/*C와 마찬가지로 0만 거짓입니다.*/ +num = 0 +if(!num) {print "false\n"} + +/*C와 달리 bc에는 ?: 연산자가 없습니다. 예를 들어, + 이 코드 블록은 오류를 발생시킵니다: +a = (num) ? 1 : 0 +그러나 하나를 시뮬레이션할 수 있습니다:*/ +a = (num) && (1) || (0) /*&&는 and, ||는 or입니다*/ + +/*For 루프*/ +num = 0 +for(i = 1; i <= 100; i++) {/*C for 루프와 유사합니다.*/ + num += i +} + + /*2.함수 및 배열*/ +define fac(n) { /*define을 사용하여 함수를 정의합니다.*/ + if(n == 1 || n == 0) { + return 1 /*값을 반환합니다*/ + } + return n * fac(n - 1) /*재귀가 가능합니다*/ +} + +/*클로저 및 익명 함수는 불가능합니다.*/ + +num = fac(4) /*24*/ + +/*다음은 지역 변수의 예입니다:*/ +define x(n) { + auto x + x = 1 + return n + x +} +x(3) /*4*/ +print x /*함수 밖에서는 x에 접근할 수 없습니다.*/ +/*배열은 C 배열과 동일합니다.*/ +for(i = 0; i <= 3; i++) { + a[i] = 1 +} +/*이렇게 접근합니다:*/ +print a[0], " ", a[1], " ", a[2], " ", a[3], "\n" +quit /*프로그램이 종료되도록 이 줄을 추가합니다. +이 줄은 선택 사항입니다.*/ +``` + +이 간단한 계산기를 즐기십시오! (또는 이 프로그래밍 언어를 정확히 말하면.) + +이 전체 프로그램은 GNU bc로 작성되었습니다. 실행하려면 ```bc learnbc.bc```를 사용하십시오. + +``` \ No newline at end of file diff --git a/ko/bf.md b/ko/bf.md index 4b441ca6c0..56028e3f49 100644 --- a/ko/bf.md +++ b/ko/bf.md @@ -6,18 +6,22 @@ contributors: translators: - ["JongChan Choi", "http://0xABCDEF.com/"] - ["Peter Lee", "http://peterjlee.com/"] + - ["Taeyoon Kim", "https://github.com/partrita"] --- -Brainfuck(문장을 시작하는 단어가 아닌이상 첫글자는 대문자를 사용하지 않습니다)은 -여덟가지 명령어만으로 튜링-완전한 최소주의 프로그래밍 언어입니다. +Brainfuck(문장의 시작 단어가 아닌 이상 첫 글자는 대문자를 사용하지 않습니다)은 +여덟 가지 명령어만으로 튜링-완전한 최소주의 프로그래밍 언어입니다. ```bf "><+-.,[]" 이외의 문자들은 무시됩니다. (쌍따옴표는 제외) +``` 브레인퍽은 30,000 칸 짜리의 0으로 초기화된 배열과, 현재 칸을 가르키는 포인터로 표현됩니다. -여덟가지의 명령어는 다음과 같습니다: +여덟 가지의 명령어는 다음과 같습니다: + +```bf + : 포인터가 가르키는 현재 칸의 값을 1 증가시킵니다. - : 포인터가 가르키는 현재 칸의 값을 1 감소시킵니다. > : 포인터가 다음 칸(오른쪽 칸)을 가르키도록 이동시킵니다. @@ -29,55 +33,62 @@ Brainfuck(문장을 시작하는 단어가 아닌이상 첫글자는 대문자 ] : 현재 칸의 값이 0이면 다음 명령어로 넘어갑니다. 0이 아니면 짝이 맞는 [ 명령으로 다시 돌아갑니다. -[이랑 ]은 while 루프를 만들어냅니다. 무조건, 짝이 맞아야 합니다. +[와 ]는 while 루프를 만듭니다. 무조건 짝이 맞아야 합니다. +``` -몇가지 간단한 브레인퍽 프로그램을 보겠습니다. +몇 가지 간단한 브레인퍽 프로그램을 보겠습니다. +```bf ++++++ [ > ++++++++++ < - ] > +++++ . +``` 이 프로그램은 문자 'A'를 출력합니다. 처음에는, 반복할 횟수를 정하기 위한 값을 -만들기 위해 첫번째 칸의 값을 6으로 증가시킵니다. 그리고 루프로 들어가서([) -두번째 칸으로 넘어갑니다. 루프 안에서는 두번째 칸의 값을 10 증가시키고, -다시 첫번째 칸으로 넘어가서 값을 1 감소시킵니다. 이 루프는 여섯번 돕니다. -(첫번째 칸의 값을 6번 감소시켜서 0이 될 때 까지는 ] 명령을 만날 때마다 +만들기 위해 첫 번째 칸의 값을 6으로 증가시킵니다. 그리고 루프로 들어가서([) +두 번째 칸으로 넘어갑니다. 루프 안에서는 두 번째 칸의 값을 10 증가시키고, +다시 첫 번째 칸으로 넘어가서 값을 1 감소시킵니다. 이 루프는 여섯 번 돕니다. +(첫 번째 칸의 값을 6번 감소시켜서 0이 될 때까지는 ] 명령을 만날 때마다 루프의 시작 지점으로 돌아갑니다) -이 시점에서, 두번째 칸의 값은 60이고, 포인터는 값이 0인 첫번째 칸에 위치합니다. -여기서 두번째 칸으로 넘어간 다음 값을 5 증가시키면 두번째 칸의 값이 65가 되고, -65는 문자 'A'에 대응하는 아스키 코드이기 때문에, 두번째 칸의 값을 출력하면 +이 시점에서, 두 번째 칸의 값은 60이고, 포인터는 값이 0인 첫 번째 칸에 위치합니다. +여기서 두 번째 칸으로 넘어간 다음 값을 5 증가시키면 두 번째 칸의 값이 65가 되고, +65는 문자 'A'에 대응하는 아스키 코드이기 때문에, 두 번째 칸의 값을 출력하면 터미널에 'A'가 출력됩니다. +```bf , [ > + < - ] > . +``` -이 프로그램은 사용자로부터 문자 하나를 입력받아 첫번째 칸에 집어넣습니다. -그리고 루프에 들어가서, 두번째 칸으로 넘어가 값을 한 번 증가시킨 다음, -다시 첫번째 칸으로 넘어가서 값을 한 번 감소시킵니다. -이는 첫번째 칸의 값이 0이 될 때까지 지속되며, -두번째 칸은 첫번째 칸이 갖고있던 값을 가지게 됩니다. -루프가 종료되면 포인터는 첫번째 칸을 가르키기 때문에 두번째 칸으로 넘어가고, +이 프로그램은 사용자로부터 문자 하나를 입력받아 첫 번째 칸에 집어넣습니다. +그리고 루프에 들어가서, 두 번째 칸으로 넘어가 값을 한 번 증가시킨 다음, +다시 첫 번째 칸으로 넘어가서 값을 한 번 감소시킵니다. +이는 첫 번째 칸의 값이 0이 될 때까지 지속되며, +두 번째 칸은 첫 번째 칸이 갖고 있던 값을 가지게 됩니다. +루프가 종료되면 포인터는 첫 번째 칸을 가르키기 때문에 두 번째 칸으로 넘어가고, 해당 아스키 코드에 대응하는 문자를 출력합니다. -또한 공백문자는 순전히 가독성을 위해서 작성되었다는 것을 기억하세요. +또한 공백 문자는 순전히 가독성을 위해서 작성되었다는 것을 기억하세요. 다음과 같이 작성해도 똑같이 돌아갑니다: +```bf ,[>+<-]>. +``` 한 번 돌려보고 아래의 프로그램이 실제로 무슨 일을 하는지 맞춰보세요: +```bf ,>,< [ > [ >+ >+ << -] >> [- << + >>] <<< -] >> +``` 이 프로그램은 두 개의 숫자를 입력받은 뒤, 그 둘을 곱합니다. -위 코드는 일단 두 번의 입력을 받고, 첫번째 칸의 값만큼 바깥 루프를 돕니다. -그리고 루프 안에서 다시 두번째 칸의 값만큼 안쪽의 루프를 돕니다. -그리고 그 루프에서는 세번째 칸의 값을 증가시키는데, 문제가 하나 있습니다: -내부 루프가 한 번 끝나게 되면 두번째 칸의 값은 0이 됩니다. +위 코드는 일단 두 번의 입력을 받고, 첫 번째 칸의 값만큼 바깥 루프를 돕니다. +그리고 루프 안에서 다시 두 번째 칸의 값만큼 안쪽의 루프를 돕니다. +그리고 그 루프에서는 세 번째 칸의 값을 증가시키는데, 문제가 하나 있습니다: +내부 루프가 한 번 끝나게 되면 두 번째 칸의 값은 0이 됩니다. 그럼 다시 바깥 루프를 돌 때에 안쪽의 루프를 돌지 않게 되는데, 이를 해결하려면 -네번째 칸의 값도 같이 증가시킨 다음, 그 값을 두번째 칸으로 옮기면 됩니다. -그러면 세번째 칸에 곱셈의 결과가 남습니다. -``` +네 번째 칸의 값도 같이 증가시킨 다음, 그 값을 두 번째 칸으로 옮기면 됩니다. +그러면 세 번째 칸에 곱셈의 결과가 남습니다. 여기까지 브레인퍽이었습니다. 참 쉽죠? 재미삼아 브레인퍽 프로그램이나 다른 언어로 브레인퍽 인터프리터를 작성해보세요. -인터프리터 구현은 간단한 편인데, -사서 고생하는 것을 즐기는 편이라면 한 번 작성해보세요… 브레인퍽으로. +인터프리터 구현은 간단한 편인데, 사서 고생하는 것을 즐기는 편이라면 한 번 작성해보세요. 브레인퍽으로. \ No newline at end of file diff --git a/ko/bqn.md b/ko/bqn.md new file mode 100644 index 0000000000..a0d5600dde --- /dev/null +++ b/ko/bqn.md @@ -0,0 +1,286 @@ +--- +name: BQN +filename: learnbqn.bqn +contributors: + - ["Raghu Ranganathan", "https://github.com/razetime"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- +BQN은 APL 전통의 번거로운 측면을 제거하는 것을 목표로 하는 현대적인 배열 언어(APL과 유사)입니다. + +이러한 코드 예제는 REPL에서 시도해 보는 것이 좋습니다. [온라인 REPL](https://mlochbaum.github.io/BQN/try.html)은 키보드와 쉽게 접근할 수 있는 도움말이 함께 제공되므로 빠른 시작에 권장됩니다. 로컬 설치를 위해 [CBQN](https://github.com/dzaima/CBQN)을 빌드해 볼 수 있지만 키보드 설정이 필요합니다. + +```bqn +# 이것은 주석입니다. +# 문자 ','와 `⋄`는 문장 구분 기호입니다. + +################## +# 주요 데이터 타입 # +################## + +# 숫자 +1,2,3,4 +¯1,¯2,¯3 # 음수는 높은 빼기 기호로 작성됩니다. +π,∞,¯π,¯∞ # 파이와 무한대는 정의된 상수입니다. +1_234_456 # 숫자 사이에 밑줄을 추가할 수 있습니다. + # 이것은 값을 변경하지 않습니다. +1.3E4 # 과학적 표기법이 지원됩니다. + +# 문자 +'a','⥊' +' +' # 예, 문자 리터럴에 *어떤* 문자든 넣을 수 있습니다. +@ # 널 문자('\0' in C) +# 배열 +1‿2‿3 # 스트랜딩, 간단한 목록에 좋습니다. +⟨1,2,3⟩ # 일반 목록 표기법 +⟨1‿2,2‿3⟩ # 둘 다 혼합 가능 +[1‿2,2‿3] # 배열 표기법 + # 배열은 하위 목록을 포함하는 것과 달리 다차원입니다. + # 직사각형 모양이어야 합니다(트리 구조가 아닌 그리드 구조). +[1‿2‿3,4‿5] # 따라서 이것은 유효하지 않습니다. + # Numpy, MATLAB 및 유사한 언어에서 익숙할 수 있습니다. +"asdf" # 문자 배열(문자열) +"newline +separated" # 줄 바꿈 허용 +"quo""tes" # 큰따옴표를 두 번 입력하여 이스케이프 +# 함수 +1{𝕨+𝕩}3 # 모든 함수는 중위입니다. + # 𝕨은 왼쪽 인수, 𝕩는 오른쪽 인수입니다. +{-𝕩}5 # 𝕨은 생략할 수 있습니다. +1+3 # 위와 동일 +{𝕊𝕩} # 𝕊는 재귀 호출입니다. + # (이 함수는 영원히 반복됩니다) +{𝕨 𝕊 𝕩: 𝕨+𝕩} # 함수는 헤더를 가질 수 있습니다(여기서 논의하기에는 너무 많은 경우가 있습니다). + # 헤더는 arity를 정의할 수 있습니다. +{𝕊 a‿b: a}1‿2 # 그리고 기본 패턴 매칭도 수행합니다. + # (1을 반환) + +# 수정자(고차 함수) +{𝕗,𝔽,𝕘,𝔾} # 𝔽와 𝔾는 호출 가능한 함수로서의 피연산자입니다. + # 𝕗와 𝕘는 값으로서의 피연산자입니다. +{𝔽𝕩} # 1-수정자는 𝔽/𝕗만 사용합니다. +˜,˘,¨,⁼,⌜ # 기본 1-수정자는 위첨자입니다. +{𝕨𝔽𝔾𝕩} # 2-수정자는 본문이나 헤더에서 𝔽/𝕗와 𝔾/𝕘를 모두 사용해야 합니다. +⊸,∘,○,⟜ # 기본 2-수정자는 모두 원을 가지고 있습니다. ++{⟨𝕗⟩} # ⟨ + ⟩를 반환합니다. +1-{𝔽 𝕨 𝔾 𝕩 }×2 # ¯2를 반환합니다(연산자도 *중위*입니다). + # (1 -○× 2와 동일) + +# 트레인(특수 형태의 함수 구성) +(+´÷≠) # 평균(하지만 어떻게?) +# 위의 트레인은 F G H 트레인이며, +# (F G H) 𝕩 → (F 𝕩) G (H 𝕩) +# F ← +´, G ← ÷, H ← ≠ +# 명시적 형식에서는 다음과 같습니다. +{(+´𝕩)÷≠𝕩} +# 두 번째 패턴은 (f g) 𝕩 → f g 𝕩입니다. +# 더 긴 트레인은 상수와 Nothing(·)을 포함하는 이러한 패턴의 복잡한 배열입니다. +# 트레인에 대한 자세한 내용은 https://mlochbaum.github.io/BQN/doc/train.html에서 읽어보십시오. + +# 평가 순서: +# BQN은 *함수*를 지배하는 우선 순위 규칙 없이 오른쪽에서 왼쪽으로 함수를 평가합니다. 함수는 +# 주류 언어에서 연산자라고 부르는 것입니다. +1÷2+3 # 1÷(2+3) = 0.2 +(1÷2)+3 # ((1÷2)+3) = 1.5 + +# 수정자: +# 수정자는 고차 함수이며 함수보다 더 강하게 바인딩됩니다. 수정자는 왼쪽에서 오른쪽으로 실행됩니다. +# 수정자는 비함수 인수를 사용할 수 있습니다. 예: 상수(`˙`) ++ +1+˜2+○-∘×3 # 1(+˜)(2((+○-)∘×)3) + +# 변수 +# 변수의 대소문자는 BQN이 의미를 결정하는 데 중요하므로 BQN 변수는 *대소문자를 구분하지 않습니다*. +# 변수가 작성된 대소문자는 BQN이 해석하는 방식을 변경할 수 있습니다. +# 예: `F`는 호출 가능한 함수로서의 값을 참조하는 반면, `f`는 값으로서의 동일한 변수를 참조합니다. +# 변수 할당은 `←`로 수행됩니다. 변수는 값에 따라 명명 규칙을 가집ed니다: +subject ← 1‿2‿3 # 배열, 단일 값, 네임스페이스가 여기에 속합니다. + # 이름은 소문자로 시작해야 합니다. +Function ← {𝕨+𝕩} # 기본 및 사용자 정의 함수가 여기에 속하며, 단항 및 이항 모두입니다. + # 대문자로 시작합니다. +_1modifier ← {𝕨𝔽𝕩} # 밑줄로 시작합니다. +_2modifier_ ← {𝔽𝕨𝔾𝕩} # 밑줄로 시작하고 끝납니다. +# 변수 수정은 `↩`로 수행됩니다. 기존 이름은 `←`로 재할당할 수 없습니다. +Func ↩ {"Hello"∾𝕩} +array_or_atom +↩ 2 # 수정에 이항 함수를 사용할 수 있습니다. + #≡ 3‿4‿5 +array_or_atom -↩ # 또는 단항 함수. + #≡ ¯3‿¯4‿¯5 +# 모든 함수가 중위이므로 수정에 자신의 함수를 사용할 수 있습니다: +array_or_atom {2⋆𝕩}↩ #≡ ⟨ 0.125, 0.0625, 0.03125 ⟩ + +################## +# BQN 기본 요소 # +################## +# BQN의 모든 기본 기본 요소는 단일 문자 길이입니다. 예제는 https://mlochbaum.github.io/BQN/help/index.html을 참조하십시오. +# 여기서는 각 섹션에서 몇 가지 기본 요소를 살펴보겠습니다. 자세한 설명은 문서를 참조하십시오. + +# 기본 함수 +# 모든 BQN 함수는 가변적이며 하나 또는 두 개의 인수를 사용할 수 있습니다. 기본 함수에는 단항 및 이항 오버로드가 모두 있습니다. +# 일반적으로 함수의 두 오버로드는 관련이 있습니다. + +## 산술 함수 ++, -, ×, ÷ # 더하기, 빼기, 부호/곱하기, 역수/나누기, '*'는 곱셈을 수행하지 않습니다. + # ⌊∘÷는 바닥 나눗셈을 수행합니다. +√, ⋆ # 제곱근/N제곱근, e^x/거듭제곱 +# 모든 산술 함수는 벡터화됩니다: +1 + 2‿3‿4 #≡ 3‿4‿5 +1‿2‿3 + 2‿3‿4 #≡ 3‿5‿7 +# 문자 산술(+ 및 -만): +"abc"+3 #≡ "def" +'a'-'d' #≡ ¯3 + +## 논리 함수 +∧, ∨, ¬ # 부울의 경우 1 또는 0을 반환합니다. +≤, <, >, ≥, = # 벡터화 비교 +≡, ≢ # 비벡터화 비교 + +## 배열 조작 함수 +↕ # 범위 만들기 +∾, ≍, ⋈ # 배열을 함께 결합 +a←1‿2‿3,b←4‿5 # a와 b를 가져옵니다. +a∾b #≡ 1‿2‿3‿4‿5 +a≍b # a와 b가 다차원이 아니므로 이전과 동일합니다. + # 다차원 배열에 대한 a ⋈와 유사하게 추가 차원을 추가합니다. +a⋈b #≡ ⟨1‿2‿3, 4‿5⟩ +⊑, ⊏ # 인덱싱 +1⊑1‿2‿3 #≡ 2 (BQN은 0-인덱싱됨) +1‿2⊏1‿2‿3 #≡ 2‿3 (여러 인덱스의 경우) +↑, ↓ # 배열의 접두사, 접미사 가져오기. + # 함께 슬라이싱에 사용할 수 있습니다. +⥊ # 새 배열을 만들기 위해 항목을 재구성/반복 + +# 기본 1-수정자 +## 반복 조합기 +¨, ˘, ⌜ # 매핑/지핑 +´, ˝ # 오른쪽에서 접기 +` # 왼쪽에서 스캔 + +## 일반 조합기 +˜ # 인수 복제/인수 교환 - 매우 유용합니다! +˙ # 상수 함수 만들기 +1 -˜ 2 #≡ 2 - 1 ++˜ 2 #≡ 2 + 2 + +# 기본 2-수정자 +## 제어 흐름 +◶ # 함수 목록에서 선택 +⍟ # n번 반복 + +## 일반 조합기 +⊸, ⟜ # 후크, 후크프 +∘, ○ # 간단한 함수 구성 + +########## +# 블록 # +########## +# {}로 구분된 코드 +# 어휘적으로 범위 지정됨 +# 자세한 내용은 https://mlochbaum.github.io/BQN/doc/block.html을 참조하십시오. +# 헤더를 가질 수 있으며, 이는 블록이 무엇이어야 하는지를 명시적으로 정의하는 방법입니다. +# 헤더가 없는 블록은 특수 변수(𝕨, 𝕩, ...)에서 자동으로 추론됩니다. + +# 함수 블록 +# 암시적 변수(대문자는 함수): +# - 𝕨, 𝕎 왼쪽 인수 +# - 𝕩, 𝕏 오른쪽 인수 +# - 𝕤, 𝕊 블록 자체를 나타냅니다. +# 선택 사항: 다음에 따라 트리거되는 하나 이상의 헤더 +# - 패턴 일치(':') o +# - 조건('?') (if-then-else와 유사) + +{ # 헤더를 사용하는 팩토리얼: + 𝕊 0: 1; + 𝕊 𝕩: 𝕩×𝕊 𝕩-1 +} +{ # 술어를 사용하는 팩토리얼 + 𝕩<2 ? 1; # if-else 패턴과 유사합니다. + 𝕩×𝕊 𝕩-1 +} + +# 수정자 블록 +# 1-수정자 및 2-수정자를 생성하며, 별도의 유형을 가집니다. +# 암시적 변수(대문자는 함수): +# - 필요한 경우 𝕨 및 𝕩를 가집니다. +# - 𝕗, 𝔽 왼쪽 피연산자 +# - 𝕘, 𝔾 오른쪽 피연산자(2-수정자에서만) +# - 𝕣 블록 자체를 나타냅니다* (규칙에 따라 밑줄 필요) +# 함수와 동일한 헤더 규칙. +{ 𝕨=0 ? 𝔽 𝕩; 𝔾 𝕩 } # 왼쪽 인수가 0인지 여부에 따라 𝔽 또는 𝔾를 실행합니다. + +# 네임스페이스 블록 +# 필드가 있는 불변 네임스페이스 생성 +# 접근 가능한 필드에 대해 내보내기(`⇐`) 필요 +# 필드 접근에 '.' 사용 +n←{ + A←+ + b⇐4 +} +n.b #≡ 4 +n.a # ERROR + +# 즉시 블록 +# 인수 없음 +# 내부 코드를 실행하고 마지막 문을 반환합니다. +# 종종 이상한 오류의 원인이 됩니다. +# 다른 블록과 쉽게 혼동될 수 있습니다. +# 범위 지정 문제를 피하는 데 좋습니다. +{ + 1‿2‿3 +} +{+} # 값을 함수로 반환하는 트릭 +#################### +# 기본 구성 # +#################### +# 함수형 프로그래밍 +# `¨`는 이전에 논의한 바와 같이 매핑에 사용됩니다: +{𝕩∾2}¨1‿2‿3 #≡ ⟨1‿2,2‿2,3‿2⟩ +# ⋈¨는 쌍을 생성하는 일반 zip입니다. +# `¨`는 두 개의 인수가 있는 경우 zipWith로 작동합니다: +1‿2‿3 {⟨𝕩+2,2⥊𝕨⟩} 4‿5‿6 #≡ ⟨⟨6,1‿1⟩,⟨7,2‿2⟩,⟨8,3‿3⟩⟩ +# `/`는 복제이며, 필터링을 *포함*하여 여러 목적으로 사용됩니다. +# 𝕩의 요소는 𝕨의 해당 숫자로 반복됩니다. +1‿2‿3‿0/4‿5‿6‿7 #≡ 4‿5‿5‿6‿6‿6 +# 간단한 필터 관용구는 F⊸/입니다: +{2|𝕩}⊸/67‿42‿83 # 홀수 요소 유지 + #≡ 67‿83 + +# 조건문 +# 조건문을 정의하는 두 가지 주요 방법이 있습니다. +## 술어 헤더 +{ + 𝕩 > 2: "2보다 큼"; + 𝕩 < 2: "2보다 작음"; + "2와 같음" +} + +## 선택(함수 기반) +# - 2-수정자 +# - 𝔾: 본문 역할을 하는 함수 목록 +# - 𝔽: 𝔾에서 선택할 함수를 지정하는 조건 함수 +# 위와 동일한 조건문은 다음과 같습니다: +{⊑/⟨𝕩>2, 𝕩<2, 𝕩=2⟩}◶⟨ + {𝕊: "2보다 큼"} + {𝕊: "2보다 작음"} + {𝕊: "2와 같음"} +⟩ + +## 조건문에 대한 일부 도우미 +If ← {𝕏⍟𝕎@}´ # If ⟨조건, 블록⟩으로 사용됨 +IfElse ← {c‿T‿F: c◶F‿T@} # IfElse ⟨조건, 블록, Else블록⟩으로 사용됨 + +# 반복 +# 무한 반복의 기본 형태는 재귀입니다(𝕊로 수행됨). +# BQN은 꼬리 호출을 제거하지 않지만, while 관용구를 사용하여 이 문제를 해결할 수 있습니다: +While ← {𝕩{𝔽⍟𝔾∘𝔽_𝕣_𝔾∘𝔽⍟𝔾𝕩}𝕨@}´ # While 1‿{... 영원히 실행} +DoWhile ← {𝕏@ ⋄ While 𝕨‿𝕩}´ +# For 루프는 ¨로 수행할 수 있으며, 함수는 순수할 필요가 없습니다. +``` + +## 더 배울 준비가 되셨습니까? + +- [빠른 시작 가이드](https://mlochbaum.github.io/BQN/doc/quick.html) +- [전체 길이, 설명된 문서](https://mlochbaum.github.io/BQN/doc/index.html) +- [짧은 문서](https://mlochbaum.github.io/BQN/help/index.html) +- [BQN 커뮤니티!](https://mlochbaum.github.io/BQN/community/index.html) \ No newline at end of file diff --git a/ko/c++.md b/ko/c++.md new file mode 100644 index 0000000000..7504cb8efc --- /dev/null +++ b/ko/c++.md @@ -0,0 +1,1175 @@ +--- +name: C++ +filename: learncpp.cpp +contributors: + - ["Steven Basart", "https://github.com/xksteven"] + - ["Matt Kline", "https://github.com/mrkline"] + - ["Geoff Liu", "http://geoffliu.me"] + - ["Connor Waters", "https://github.com/connorwaters"] + - ["Ankush Goyal", "https://github.com/ankushg07"] + - ["Jatin Dhankhar", "https://github.com/jatindhankhar"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +C++는 시스템 프로그래밍 언어로, +[발명가 Bjarne Stroustrup에 따르면](https://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Keynote), +다음과 같이 설계되었습니다. + +- "더 나은 C"가 되기 위해 +- 데이터 추상화 지원 +- 객체 지향 프로그래밍 지원 +- 제네릭 프로그래밍 지원 + +구문이 최신 언어보다 더 어렵거나 복잡할 수 있지만, +프로세서에서 직접 실행할 수 있는 네이티브 명령으로 컴파일되고 +C와 같이 하드웨어를 긴밀하게 제어할 수 있으면서도 제네릭, 예외, 클래스와 같은 +고급 기능을 제공하기 때문에 널리 사용됩니다. +이러한 속도와 기능의 조합으로 C++는 +가장 널리 사용되는 프로그래밍 언어 중 하나가 되었습니다. + +```c++ +////////////////// +// C와의 비교 +////////////////// + +// C++는 거의 C의 상위 집합이며 +// 변수 선언, 기본 타입 및 함수에 대한 기본 구문을 공유합니다. + +// C에서와 마찬가지로 프로그램의 진입점은 +// 정수 반환 타입을 가진 main이라는 함수입니다. +// 이 값은 프로그램의 종료 상태로 사용됩니다. +// 자세한 내용은 https://en.wikipedia.org/wiki/Exit_status를 참조하십시오. +int main(int argc, char** argv) +{ + // 명령줄 인수는 C에서와 동일한 방식으로 + // argc 및 argv로 전달됩니다. + // argc는 인수 수를 나타내고, + // argv는 인수를 나타내는 C 스타일 문자열(char*)의 배열입니다. + // 첫 번째 인수는 프로그램을 호출한 이름입니다. + // 인수에 관심이 없는 경우 argc 및 argv를 생략할 수 있으며, + // 함수 시그니처는 int main()이 됩니다. + + // 종료 상태 0은 성공을 나타냅니다. + return 0; +} + +// 그러나 C++는 다음과 같은 몇 가지 면에서 다릅니다: + +// C++에서 문자 리터럴은 char이므로 크기는 1입니다. +sizeof('c') == sizeof(char) + +// C에서 문자 리터럴은 int이므로 크기는 4입니다. +sizeof('c') == sizeof(int) + + +// C++는 엄격한 프로토타이핑을 사용합니다. +void func(); // 인수를 받지 않는 함수 +void func(void); // 이전과 동일 + +// C에서 +void func(); // 알 수 없는 타입의 인수를 임의 개수 받을 수 있는 함수 +void func(void); // 인수를 받지 않는 함수 + +// C++에서 NULL 대신 nullptr 사용 +int* ip = nullptr; + +// 대부분의 C 표준 헤더는 C++에서 사용할 수 있습니다. +// C 헤더는 일반적으로 .h로 끝나지만, +// C++ 헤더는 "c" 접두사가 붙고 ".h" 접미사가 없습니다. + +// C++ 표준 버전: +#include + +// C 표준 버전: +#include + +int main() +{ + printf("Hello, world!\n"); + return 0; +} + +/////////////////////// +// 함수 오버로딩 +/////////////////////// + +// C++는 함수 오버로딩을 지원합니다. +// 각 함수가 다른 매개변수를 사용하는 경우에 한합니다. + +void print(char const* myString) +{ + printf("String %s\n", myString); +} + +void print(int myInt) +{ + printf("My int is %d\n", myInt); +} + +int main() +{ + print("Hello"); // void print(const char*)로 확인됩니다. + print(15); // void print(int)로 확인됩니다. +} + +///////////////////////////// +// 기본 함수 인수 +///////////////////////////// + +// 호출자가 제공하지 않은 경우 함수에 대한 기본 인수를 제공할 수 있습니다. + +void doSomethingWithInts(int a = 1, int b = 4) +{ + // 여기서 int로 무언가를 합니다. +} + +int main() +{ + doSomethingWithInts(); // a = 1, b = 4 + doSomethingWithInts(20); // a = 20, b = 4 + doSomethingWithInts(20, 5); // a = 20, b = 5 +} + +// 기본 인수는 인수 목록의 끝에 있어야 합니다. + +void invalidDeclaration(int a = 1, int b) // 오류! +{ +} + + +///////////// +// 네임스페이스 +///////////// + +// 네임스페이스는 변수, 함수 및 기타 선언에 대한 별도의 범위를 제공합니다. +// 네임스페이스는 중첩될 수 있습니다. + +namespace First { + namespace Nested { + void foo() + { + printf("This is First::Nested::foo\n"); + } + } // 네임스페이스 Nested 끝 +} // 네임스페이스 First 끝 + +namespace Second { + void foo() + { + printf("This is Second::foo\n"); + } + void bar() + { + printf("This is Second::bar\n"); + } +} + +void foo() +{ + printf("This is global foo\n"); +} + +int main() +{ + // 네임스페이스 Second의 모든 기호를 현재 범위에 포함합니다. 참고 + // bar()는 작동하지만 foo()를 사용하는 것은 더 이상 작동하지 않습니다. + // 이제 네임스페이스 Second의 foo를 호출하는지 최상위 수준의 foo를 호출하는지 + // 모호하기 때문입니다. + using namespace Second; + + bar(); // "This is Second::bar" 출력 + Second::foo(); // "This is Second::foo" 출력 + First::Nested::foo(); // "This is First::Nested::foo" 출력 + ::foo(); // "This is global foo" 출력 +} + +/////////////// +// 입력/출력 +/////////////// + +// C++ 입력 및 출력은 스트림을 사용합니다. +// cin, cout 및 cerr은 stdin, stdout 및 stderr을 나타냅니다. +// <<는 삽입 연산자이고 >>는 추출 연산자입니다. + +#include // I/O 스트림 포함 + +int main() +{ + int myInt; + + // stdout(또는 터미널/화면)에 출력 + // std::cout은 std 네임스페이스에 대한 액세스를 참조합니다. + std::cout << "Enter your favorite number:\n"; + // 입력 받기 + std::cin >> myInt; + + // cout도 형식을 지정할 수 있습니다. + std::cout << "Your favorite number is " << myInt << '\n'; + // "Your favorite number is " 출력 + + std::cerr << "Used for error messages"; + + // 새 줄로 문자열 스트림 버퍼 플러시 + std::cout << "I flushed it away" << std::endl; +} + +////////// +// 문자열 +////////// + +// C++의 문자열은 객체이며 많은 멤버 함수를 가집니다. +#include + +std::string myString = "Hello"; +std::string myOtherString = " World"; + +// +는 연결에 사용됩니다. +std::cout << myString + myOtherString; // "Hello World" + +std::cout << myString + " You"; // "Hello You" + +// C++ 문자열 길이는 string::length() 또는 string::size()에서 찾을 수 있습니다. +cout << myString.length() + myOtherString.size(); // 11 (= 5 + 6) 출력. + +// C++ 문자열은 변경 가능합니다. +myString.append(" Dog"); +std::cout << myString; // "Hello Dog" + +// C++는 cstring을 사용하여 관련 함수로 C 스타일 문자열을 처리할 수 있습니다. +#include + +char myOldString[10] = "Hello CPP"; +cout << myOldString; +cout << "Length = " << strlen(myOldString); // Length = 9 + +///////////// +// 참조 +///////////// + +// C의 포인터 외에도 +// C++에는 _참조_가 있습니다. +// 이들은 한 번 설정되면 재할당할 수 없는 포인터 유형이며 +// null일 수 없습니다. +// 또한 변수 자체와 동일한 구문을 가집니다: +// 역참조에는 *가 필요하지 않으며 +// 할당에는 & (주소)가 사용되지 않습니다. + +std::string foo = "I am foo"; +std::string bar = "I am bar"; + +std::string& fooRef = foo; // foo에 대한 참조를 생성합니다. +fooRef += ". Hi!"; // 참조를 통해 foo를 수정합니다. +std::cout << fooRef; // "I am foo. Hi!" 출력 + +std::cout << &fooRef << '\n'; // foo의 주소 출력 +// "fooRef"를 재할당하지 않습니다. 이것은 "foo = bar"와 동일하며, +// foo == "I am bar" +// 이 줄 이후입니다. +fooRef = bar; +std::cout << &fooRef << '\n'; // 여전히 foo의 주소 출력 +std::cout << fooRef << '\n'; // "I am bar" 출력 + +// fooRef의 주소는 동일하게 유지됩니다. 즉, 여전히 foo를 참조합니다. + + +const std::string& barRef = bar; // bar에 대한 const 참조 생성. +// C와 마찬가지로 const 값(및 포인터 및 참조)은 수정할 수 없습니다. +barRef += ". Hi!"; // 오류, const 참조는 수정할 수 없습니다. + +// 옆길: 참조에 대해 더 이야기하기 전에 임시 객체라는 개념을 소개해야 합니다. +// 다음과 같은 코드가 있다고 가정해 보겠습니다: +std::string tempObjectFun() { ... } +std::string retVal = tempObjectFun(); + +// 두 번째 줄에서 실제로 일어나는 일은 다음과 같습니다: +// - tempObjectFun에서 문자열 객체가 반환됩니다. +// - 생성자에 대한 인수로 반환된 객체로 새 문자열이 생성됩니다. +// - 반환된 객체가 소멸됩니다. +// 반환된 객체를 임시 객체라고 합니다. 임시 객체는 +// 함수가 객체를 반환할 때마다 생성되며, 둘러싸는 표현식의 평가가 끝날 때 +// 소멸됩니다(음, 이것이 표준에서 말하는 것이지만 +// 컴파일러는 이 동작을 변경할 수 있습니다. 이러한 종류의 세부 사항에 관심이 있다면 +// "반환 값 최적화"를 찾아보십시오). 따라서 이 코드에서: +foo(bar(tempObjectFun())) + +// foo와 bar가 존재한다고 가정하면, tempObjectFun에서 반환된 객체는 +// bar에 전달되고 foo가 호출되기 전에 소멸됩니다. + +// 이제 참조로 돌아갑니다. "둘러싸는 표현식의 끝에서" 규칙의 예외는 +// 임시 객체가 const 참조에 바인딩된 경우이며, 이 경우 수명이 +// 현재 범위로 연장됩니다: + +void constReferenceTempObjectFun() { + // constRef는 임시 객체를 가져오고 이 함수가 끝날 때까지 유효합니다. + const std::string& constRef = tempObjectFun(); + ... +} + +// C++11에서 도입된 또 다른 종류의 참조는 임시 객체를 위한 것입니다. 해당 유형의 변수를 가질 수는 없지만 오버로드 확인에서 우선 순위를 가집니다: + +void someFun(std::string& s) { ... } // 일반 참조 +void someFun(std::string&& s) { ... } // 임시 객체에 대한 참조 + +std::string foo; +someFun(foo); // 일반 참조가 있는 버전을 호출합니다. +someFun(tempObjectFun()); // 임시 참조가 있는 버전을 호출합니다. + +// 예를 들어, std::basic_string에 대해 다음 두 가지 버전의 생성자를 볼 수 있습니다: +std::basic_string(const basic_string& other); +std::basic_string(basic_string&& other); + +// 아이디어는 임시 객체(어쨌든 곧 소멸될 예정)에서 새 문자열을 생성하는 경우 +// 해당 임시 문자열의 일부를 "구조"하는 더 효율적인 생성자를 가질 수 있다는 것입니다. 이 개념을 "이동 의미론"이라고 합니다. + +///////////// +// 열거형 +///////////// + +// 열거형은 코드의 가독성과 시각화를 용이하게 하기 위해 상수에 값을 할당하는 방법입니다. +enum ECarTypes +{ + Sedan, + Hatchback, + SUV, + Wagon +}; + +ECarTypes GetPreferredCarType() +{ + return ECarTypes::Hatchback; +} + +// C++11부터는 열거형에 타입을 할당하는 쉬운 방법이 있어 데이터 직렬화 및 +// 원하는 타입과 해당 상수 간의 열거형 변환에 유용할 수 있습니다. +enum ECarTypes : uint8_t +{ + Sedan, // 0 + Hatchback, // 1 + SUV = 254, // 254 + Hybrid // 255 +}; + +void WriteByteToFile(uint8_t InputValue) +{ + // InputValue를 파일에 직렬화합니다. +} + +void WritePreferredCarTypeToFile(ECarTypes InputCarType) +{ + // 열거형은 선언된 열거형 타입으로 인해 uint8_t로 암시적으로 변환됩니다. + WriteByteToFile(InputCarType); +} + +// 반면에 열거형이 정수 타입이나 다른 열거형으로 우연히 캐스팅되는 것을 원하지 않을 수 있으므로 +// 암시적으로 변환되지 않는 열거형 클래스를 만들 수 있습니다. +enum class ECarTypes : uint8_t +{ + Sedan, // 0 + Hatchback, // 1 + SUV = 254, // 254 + Hybrid // 255 +}; + +void WriteByteToFile(uint8_t InputValue) +{ + // InputValue를 파일에 직렬화합니다. +} + +void WritePreferredCarTypeToFile(ECarTypes InputCarType) +{ + // 열거형이 "열거형 클래스"로 선언되었기 때문에 ECarTypes가 uint8_t임에도 불구하고 컴파일되지 않습니다! + WriteByteToFile(InputCarType); +} + +////////////////////////////////////////// +// 클래스 및 객체 지향 프로그래밍 +////////////////////////////////////////// + +// 클래스의 첫 번째 예 +#include + +// 클래스를 선언합니다. +// 클래스는 일반적으로 헤더(.h 또는 .hpp) 파일에 선언됩니다. +class Dog { + // 멤버 변수 및 함수는 기본적으로 private입니다. + std::string name; + int weight; + +// 이 뒤에 오는 모든 멤버는 public입니다. +// "private:" 또는 "protected:"가 나올 때까지. +public: + + // 기본 생성자 + Dog(); + + // 멤버 함수 선언(구현은 나중에) + // 참고로 여기서는 std::string을 사용하고 + // 위에 using namespace std;를 배치하지 않습니다. + // 헤더에 "using namespace" 문을 절대 넣지 마십시오. + void setName(const std::string& dogsName); + + void setWeight(int dogsWeight); + + // 객체의 상태를 수정하지 않는 함수는 + // const로 표시해야 합니다. + // 이렇게 하면 객체에 대한 const 참조가 주어졌을 때 호출할 수 있습니다. + // 또한 파생 클래스에서 재정의하려면 함수를 명시적으로 _virtual_로 + // 선언해야 합니다. + // 성능상의 이유로 함수는 기본적으로 가상이 아닙니다. + virtual void print() const; + + // 함수는 클래스 본문 내에서도 정의할 수 있습니다. + // 이렇게 정의된 함수는 자동으로 인라인됩니다. + void bark() const { std::cout << name << " barks!\n"; } + + // 생성자와 함께 C++는 소멸자를 제공합니다. + // 이들은 객체가 삭제되거나 범위를 벗어날 때 호출됩니다. + // 이것은 RAII와 같은 강력한 패러다임을 가능하게 합니다. + // (아래 참조) + // 클래스에서 파생될 경우 소멸자는 가상이어야 합니다. + // 가상이 아닌 경우 파생 클래스의 소멸자는 + // 기본 클래스 참조 또는 포인터를 통해 객체가 소멸될 때 + // 호출되지 않습니다. + virtual ~Dog(); + +}; // 클래스 정의 뒤에는 세미콜론이 와야 합니다. + +// 클래스 멤버 함수는 일반적으로 .cpp 파일에 구현됩니다. +Dog::Dog() +{ + std::cout << "A dog has been constructed\n"; +} + +// 객체(예: 문자열)는 수정하는 경우 참조로 전달해야 하며, +// 수정하지 않는 경우 const 참조로 전달해야 합니다. +void Dog::setName(const std::string& dogsName) +{ + name = dogsName; +} + +void Dog::setWeight(int dogsWeight) +{ + weight = dogsWeight; +} + +// "virtual"은 선언에만 필요하고 정의에는 필요하지 않습니다. +void Dog::print() const +{ + std::cout << "Dog is " << name << " and weighs " << weight << "kg\n"; +} + +Dog::~Dog() +{ + std::cout << "Goodbye " << name << '\n'; +} + +int main() { + Dog myDog; // "A dog has been constructed" 출력 + myDog.setName("Barkley"); + myDog.setWeight(10); + myDog.print(); // "Dog is Barkley and weighs 10 kg" 출력 + return 0; +} // "Goodbye Barkley" 출력 + +// 상속: + +// 이 클래스는 Dog 클래스의 모든 public 및 protected를 상속하며, +// private도 상속하지만 public 또는 protected 메서드 없이는 +// private 멤버/메서드에 직접 액세스할 수 없습니다. +class OwnedDog : public Dog { + +public: + void setOwner(const std::string& dogsOwner); + + // 모든 OwnedDog에 대한 print 함수의 동작을 재정의합니다. 자세한 내용은 + // https://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Subtyping을 참조하십시오. + // 익숙하지 않은 경우 하위 유형 다형성에 대한 일반적인 소개입니다. + // override 키워드는 선택 사항이지만 기본 클래스의 메서드를 실제로 + // 재정의하는지 확인합니다. + void print() const override; + +private: + std::string owner; +}; + +// 한편, 해당 .cpp 파일에서: + +void OwnedDog::setOwner(const std::string& dogsOwner) +{ + owner = dogsOwner; +} + +void OwnedDog::print() const +{ + Dog::print(); // 기본 Dog 클래스의 print 함수 호출 + std::cout << "Dog is owned by " << owner << '\n'; + // "Dog is and weights " 출력 + // "Dog is owned by " +} + +////////////////////////////////////////// +// 초기화 및 연산자 오버로딩 +////////////////////////////////////////// + +// C++에서는 +, -, *, /, 등과 같은 연산자의 동작을 오버로드할 수 있습니다. +// 이것은 연산자가 사용될 때마다 호출되는 함수를 정의하여 수행됩니다. + +#include +using namespace std; + +class Point { +public: + // 멤버 변수는 이 방식으로 기본값을 지정할 수 있습니다. + double x = 0; + double y = 0; + + // Point를 기본값(0, 0)으로 초기화하는 것 외에는 아무것도 하지 않는 + // 기본 생성자를 정의합니다. + Point() { }; + + // 다음 구문은 초기화 목록으로 알려져 있으며 + // 클래스 멤버 값을 초기화하는 올바른 방법입니다. + Point (double a, double b) : + x(a), + y(b) + { /* 값 초기화 외에는 아무것도 하지 않습니다. */ } + + // + 연산자를 오버로드합니다. + Point operator+(const Point& rhs) const; + + // += 연산자를 오버로드합니다. + Point& operator+=(const Point& rhs); + + // - 및 -= 연산자를 추가하는 것도 의미가 있지만, + // 간결함을 위해 생략하겠습니다. +}; + +Point Point::operator+(const Point& rhs) const +{ + // 이것과 rhs의 합인 새 점을 만듭니다. + return Point(x + rhs.x, y + rhs.y); +} + +// 할당의 가장 왼쪽 변수에 대한 참조를 반환하는 것이 좋습니다. +// `(a += b) == c`는 이 방식으로 작동합니다. +Point& Point::operator+=(const Point& rhs) +{ + x += rhs.x; + y += rhs.y; + + // `this`는 메서드가 호출되는 객체에 대한 포인터입니다. + return *this; +} + +int main () { + Point up (0,1); + Point right (1,0); + // 이것은 Point + 연산자를 호출합니다. + // Point up은 right를 매개변수로 사용하여 + (함수)를 호출합니다. + Point result = up + right; + // "Result is upright (1,1)" 출력 + std::cout << "Result is upright (" << result.x << ',' << result.y << ")\n"; + return 0; +} + +///////////// +// 템플릿 +///////////// + +// C++의 템플릿은 주로 제네릭 프로그래밍에 사용되지만, 다른 언어의 +// 제네릭 구성보다 훨씬 강력합니다. 또한 명시적 및 부분적 특수화와 +// 함수형 스타일 타입 클래스를 지원합니다. 사실, C++에 내장된 +// 튜링 완전 함수형 언어입니다! + +// 익숙할 수 있는 제네릭 프로그래밍 종류부터 시작하겠습니다. 타입 매개변수를 +// 사용하는 클래스 또는 함수를 정의하려면: +template +class Box { +public: + // 이 클래스에서 T는 다른 타입처럼 사용할 수 있습니다. + void insert(const T&) { ... } +}; + +// 컴파일 중에 컴파일러는 실제로 매개변수가 대체된 각 템플릿의 복사본을 +// 생성하므로 클래스의 전체 정의는 각 호출에 있어야 합니다. +// 이것이 헤더 파일에 전체적으로 정의된 템플릿 클래스를 보게 되는 이유입니다. + +// 스택에 템플릿 클래스를 인스턴스화하려면: +Box intBox; + +// 그리고 예상대로 사용할 수 있습니다: +intBox.insert(123); + +// 물론 템플릿을 중첩할 수 있습니다: +Box > boxOfBox; +boxOfBox.insert(intBox); + +// C++11까지는 두 '>' 사이에 공백을 두어야 했습니다. 그렇지 않으면 '>>'가 +// 오른쪽 시프트 연산자로 구문 분석됩니다. + +// 때로는 +// template +//를 보게 될 것입니다. 이 경우 'class' 키워드와 'typename' 키워드는 _대부분_ +// 서로 바꿔 사용할 수 있습니다. 전체 설명은 +// https://en.wikipedia.org/wiki/Typename을 참조하십시오. +// (예, 해당 키워드에는 자체 Wikipedia 페이지가 있습니다). + +// 마찬가지로 템플릿 함수: +template +void barkThreeTimes(const T& input) +{ + input.bark(); + input.bark(); + input.bark(); +} + +// 여기서 타입 매개변수에 대해 아무것도 지정되지 않았습니다. 컴파일러는 +// 템플릿의 모든 호출을 생성한 다음 타입 검사를 수행하므로 +// 위 함수는 const 'bark' 메서드가 있는 모든 타입 'T'에서 작동합니다! + +Dog fluffy; +fluffy.setName("Fluffy") +barkThreeTimes(fluffy); // "Fluffy barks"를 세 번 출력합니다. + +// 템플릿 매개변수는 클래스일 필요는 없습니다: +template +void printMessage() { + std::cout << "Learn C++ in " << Y << " minutes!\n"; +} + +// 그리고 더 효율적인 코드를 위해 템플릿을 명시적으로 특수화할 수 있습니다. 물론, +// 특수화의 실제 사용 사례 대부분은 이처럼 사소하지 않습니다. +// 모든 매개변수를 명시적으로 지정하더라도 함수(또는 클래스)를 템플릿으로 +// 선언해야 합니다. +template<> +void printMessage<10>() { + std::cout << "Learn C++ faster in only 10 minutes!\n"; +} + +printMessage<20>(); // "Learn C++ in 20 minutes!" 출력 +printMessage<10>(); // "Learn C++ faster in only 10 minutes!" 출력 + + +///////////// +// 예외 처리 +///////////// + +// 표준 라이브러리는 몇 가지 예외 유형을 제공합니다. +// (https://en.cppreference.com/w/cpp/error/exception 참조) +// 하지만 모든 유형을 예외로 던질 수 있습니다. +#include +#include + +// _try_ 블록 내에서 던져진 모든 예외는 후속 +// _catch_ 핸들러에 의해 잡힐 수 있습니다. +try { + // _new_를 사용하여 힙에 예외를 할당하지 마십시오. + throw std::runtime_error("A problem occurred"); +} + +// 객체인 경우 const 참조로 예외를 잡습니다. +catch (const std::exception& ex) +{ + std::cout << ex.what(); +} + +// 이전 _catch_ 블록에서 잡히지 않은 모든 예외를 잡습니다. +catch (...) +{ + std::cout << "Unknown exception caught"; + throw; // 예외를 다시 던집니다. +} + +/////// +// RAII +/////// + +// RAII는 "Resource Acquisition Is Initialization"의 약자입니다. +// C++에서 가장 강력한 패러다임으로 간주되며 +// 객체의 생성자가 해당 객체의 리소스를 획득하고 +// 소멸자가 이를 해제한다는 간단한 개념입니다. + +// 이것이 유용한 이유를 이해하려면 +// C 파일 핸들을 사용하는 함수를 고려하십시오: +void doSomethingWithAFile(const char* filename) +{ + // 우선, 아무것도 실패할 수 없다고 가정합니다. + + FILE* fh = fopen(filename, "r"); // 파일을 읽기 모드로 엽니다. + if (fh == NULL) { + // 가능한 오류 처리 + } + + doSomethingWithTheFile(fh); + doSomethingElseWithIt(fh); + + fclose(fh); // 파일 핸들을 닫습니다. +} + +// 불행히도 오류 처리로 인해 상황이 빠르게 복잡해집니다. +// fopen이 실패할 수 있고, doSomethingWithTheFile 및 +// doSomethingElseWithIt이 실패하면 오류 코드를 반환한다고 가정합니다. +// (예외는 실패를 처리하는 선호되는 방법이지만, +// 일부 프로그래머, 특히 C 배경을 가진 프로그래머는 +// 예외의 유용성에 대해 동의하지 않습니다). +// 이제 각 호출이 실패했는지 확인하고 문제가 발생하면 파일 핸들을 +// 닫아야 합니다. +bool doSomethingWithAFile(const char* filename) +{ + FILE* fh = fopen(filename, "r"); // 파일을 읽기 모드로 엽니다. + if (fh == nullptr) // 반환된 포인터는 실패 시 null입니다. + return false; // 호출자에게 실패를 보고합니다. + + // 각 함수가 실패하면 false를 반환한다고 가정합니다. + if (!doSomethingWithTheFile(fh)) { + fclose(fh); // 누출되지 않도록 파일 핸들을 닫습니다. + return false; // 오류를 전파합니다. + } + if (!doSomethingElseWithIt(fh)) { + fclose(fh); // 누출되지 않도록 파일 핸들을 닫습니다. + return false; // 오류를 전파합니다. + } + + fclose(fh); // 누출되지 않도록 파일 핸들을 닫습니다. + return true; // 성공을 나타냅니다. +} + +// C 프로그래머는 종종 goto를 사용하여 이것을 약간 정리합니다: +bool doSomethingWithAFile(const char* filename) +{ + FILE* fh = fopen(filename, "r"); + if (fh == nullptr) + return false; + + if (!doSomethingWithTheFile(fh)) + goto failure; + + if (!doSomethingElseWithIt(fh)) + goto failure; + + fclose(fh); // 파일 닫기 + return true; // 성공 표시 + +failure: + fclose(fh); + return false; // 오류 전파 +} + +// 함수가 예외를 사용하여 오류를 나타내는 경우 +// 상황이 약간 더 깨끗하지만 여전히 최적이 아닙니다. +void doSomethingWithAFile(const char* filename) +{ + FILE* fh = fopen(filename, "r"); // 파일을 shared_ptr 읽기 모드로 엽니다. + if (fh == nullptr) + throw std::runtime_error("Could not open the file."); + + try { + doSomethingWithTheFile(fh); + doSomethingElseWithIt(fh); + } + catch (...) + { + fclose(fh); // 오류가 발생하면 파일을 닫아야 합니다. + throw; // 그런 다음 예외를 다시 던집니다. + } + + fclose(fh); // 파일 닫기 + // 모든 것이 성공했습니다. +} + +// 이것을 C++의 파일 스트림 클래스(fstream) 사용과 비교하십시오. +// fstream은 소멸자를 사용하여 파일을 닫습니다. +// 위에서 언급했듯이 소멸자는 객체가 범위를 벗어날 때마다 +// 자동으로 호출됩니다. +void doSomethingWithAFile(const std::string& filename) +{ + // ifstream은 입력 파일 스트림의 약자입니다. + std::ifstream fh(filename); // 파일 열기 + + // 파일로 작업 수행 + doSomethingWithTheFile(fh); + doSomethingElseWithIt(fh); + +} // 파일은 소멸자에 의해 자동으로 여기서 닫힙니다. + +// 이것은 _엄청난_ 이점이 있습니다: +// 1. 무슨 일이 일어나든, +// 리소스(이 경우 파일 핸들)가 정리됩니다. +// 소멸자를 올바르게 작성하면 +// 핸들을 닫는 것을 잊고 리소스를 누출하는 것은 _불가능_합니다. +// 2. 코드가 훨씬 깨끗합니다. +// 소멸자는 걱정할 필요 없이 백그라운드에서 파일 닫기를 처리합니다. +// 3. 코드는 예외에 안전합니다. +// 함수 어디에서나 예외가 발생할 수 있으며 정리는 +// 여전히 발생합니다. + +// 모든 관용적인 C++ 코드는 모든 리소스에 대해 RAII를 광범위하게 사용합니다. +// 추가 예는 다음과 같습니다. +// - unique_ptr 및 shared_ptr을 사용한 메모리 +// - 컨테이너 - 표준 라이브러리 연결 리스트, +// 벡터(즉, 자체 크기 조정 배열), 해시 맵 등 +// 모두 범위를 벗어날 때 자동으로 내용을 소멸시킵니다. +// - lock_guard 및 unique_lock을 사용한 뮤텍스 + + +///////////// +// 스마트 포인터 +///////////// + +// 일반적으로 스마트 포인터는 "원시 포인터"(C에서 각각 malloc/calloc을 사용하는 "new" 사용)를 래핑하는 클래스입니다. +// 목표는 객체를 명시적으로 삭제할 필요 없이 가리키는 객체의 수명을 관리할 수 있도록 하는 것입니다. +// 용어 자체는 언급된 추상화가 있는 포인터 집합을 간단히 설명합니다. +// 객체를 삭제하는 것을 잊어버리면 발생하는 위험한 메모리 누수를 방지하기 위해 원시 포인터보다 스마트 포인터를 선호해야 합니다. + +// 원시 포인터 사용: +Dog* ptr = new Dog(); +ptr->bark(); +delete ptr; + +// 스마트 포인터를 사용하면 더 이상 객체 삭제에 대해 걱정할 필요가 없습니다. +// 스마트 포인터는 포인터에 대한 참조를 계산하는 정책을 설명합니다. +// 객체에 대한 마지막 참조가 소멸될 때 객체가 소멸됩니다. + +// "std::shared_ptr" 사용: +void foo() +{ + // 더 이상 Dog를 삭제할 필요가 없습니다. + std::shared_ptr doggo(new Dog()); + doggo->bark(); +} + +// 가능한 순환 참조에 주의하십시오!!! +// 항상 참조가 있으므로 절대 소멸되지 않습니다! +std::shared_ptr doggo_one(new Dog()); +std::shared_ptr doggo_two(new Dog()); +doggo_one = doggo_two; // p1은 p2를 참조합니다. +doggo_two = doggo_one; // p2는 p1을 참조합니다. + +// 여러 종류의 스마트 포인터가 있습니다. +// 사용해야 하는 방식은 항상 동일합니다. +// 이것은 우리를 다음과 같은 질문으로 이끕니다: 언제 각 종류의 스마트 포인터를 사용해야 할까요? +// std::unique_ptr - 객체에 대한 참조를 하나만 유지하고 싶을 때 사용합니다. +// std::shared_ptr - 동일한 객체에 대한 여러 참조를 유지하고 모든 참조가 사라졌을 때 할당 해제되도록 하고 싶을 때 사용합니다. +// std::weak_ptr - 해당 객체가 할당된 상태를 유지하지 않고 std::shared_ptr의 기본 객체에 액세스하고 싶을 때 사용합니다. +// 약한 포인터는 순환 참조를 방지하는 데 사용됩니다. + + +///////////// +// 컨테이너 +///////////// + +// 컨테이너 또는 표준 템플릿 라이브러리는 미리 정의된 템플릿입니다. +// 요소에 대한 저장 공간을 관리하고 액세스 및 조작을 위한 멤버 함수를 제공합니다. + +// 몇 가지 컨테이너는 다음과 같습니다: + +// 벡터(동적 배열) +// 런타임에 객체의 배열 또는 목록을 정의할 수 있습니다. +#include +std::string val; +std::vector my_vector; // 벡터 초기화 +std::cin >> val; + +my_vector.push_back(val); // 'val'의 값을 벡터("배열") my_vector에 푸시합니다. +my_vector.push_back(val); // 값을 벡터에 다시 푸시합니다(이제 두 개의 요소가 있음). + +// 벡터를 반복하는 두 가지 선택 사항이 있습니다: +// 클래식 루핑(인덱스 0에서 마지막 인덱스까지 벡터를 반복): +for (int i = 0; i < my_vector.size(); i++) { + std::cout << my_vector[i] << '\n'; // 벡터의 요소에 액세스하려면 연산자 []를 사용할 수 있습니다. +} + +// 또는 반복기 사용: +vector::iterator it; // 벡터에 대한 반복기 초기화 +for (it = my_vector.begin(); it != my_vector.end(); ++it) { + std::cout << *it << '\n'; +} + +// 세트 +// 세트는 특정 순서를 따르는 고유한 요소를 저장하는 컨테이너입니다. +// 세트는 다른 함수나 코드 없이 정렬된 순서로 고유한 값을 저장하는 데 매우 유용한 컨테이너입니다. + +#include +std::set ST; // int 데이터 유형의 세트를 초기화합니다. +ST.insert(30); // 세트 ST에 값 30을 삽입합니다. +ST.insert(10); // 세트 ST에 값 10을 삽입합니다. +ST.insert(20); // 세트 ST에 값 20을 삽입합니다. +ST.insert(30); // 세트 ST에 값 30을 삽입합니다. +// 이제 세트의 요소는 다음과 같습니다. +// 10 20 30 + +// 요소를 지우려면 +ST.erase(20); // 값 20을 가진 요소를 지웁니다. +// 세트 ST: 10 30 +// 세트를 반복하려면 반복기를 사용합니다. +std::set::iterator it; +for (it = ST.begin(); it != ST.end(); it++) { + std::cout << *it << '\n'; +} +// 출력: +// 10 +// 30 + +// 전체 컨테이너를 지우려면 Container_name.clear()를 사용합니다. +ST.clear(); +std::cout << ST.size(); // 세트 ST의 크기를 인쇄합니다. +// 출력: 0 + +// 참고: 중복 요소의 경우 multiset을 사용할 수 있습니다. +// 참고: 해시 세트의 경우 unordered_set을 사용하십시오. 더 효율적이지만 순서를 유지하지 않습니다. +// unordered_set은 C++11부터 사용할 수 있습니다. + +// 맵 +// 맵은 키 값과 매핑된 값의 조합으로 형성된 요소를 특정 순서에 따라 저장합니다. + +#include +std::map mymap; // 키를 char로, 값을 int로 맵을 초기화합니다. + +mymap.insert(pair('A',1)); +// 키 A에 값 1을 삽입합니다. +mymap.insert(pair('Z',26)); +// 키 Z에 값 26을 삽입합니다. + +// 반복하려면 +std::map::iterator it; +for (it = mymap.begin(); it != mymap.end(); ++it) + std::cout << it->first << "->" << it->second << '\n'; +// 출력: +// A->1 +// Z->26 + +// 키에 해당하는 값을 찾으려면 +it = mymap.find('Z'); +std::cout << it->second; + +// 출력: 26 + +// 참고: 해시 맵의 경우 unordered_map을 사용하십시오. 더 효율적이지만 순서를 유지하지 않습니다. +// unordered_map은 C++11부터 사용할 수 있습니다. + +// 비원시 값(사용자 정의 클래스)의 객체 키가 있는 컨테이너는 객체 자체 또는 함수 포인터로 비교 함수가 필요합니다. 원시에는 기본 비교기가 있지만 재정의할 수 있습니다. +class Foo { +public: + int j; + Foo(int a) : j(a) {} +}; +struct compareFunction { + bool operator()(const Foo& a, const Foo& b) const { + return a.j < b.j; + } +}; +// 이것은 허용되지 않습니다(컴파일러에 따라 다를 수 있음). +// std::map fooMap; +std::map fooMap; +fooMap[Foo(1)] = 1; +fooMap.find(Foo(1)); //true + + +/////////////////////////////////////// +// 람다 표현식(C++11 이상) +/////////////////////////////////////// + +// 람다는 호출되거나 함수에 인수로 전달되는 위치에서 바로 익명 함수 객체를 정의하는 편리한 방법입니다. + +// 예를 들어, 쌍의 두 번째 값을 사용하여 쌍의 벡터를 정렬하는 것을 고려하십시오. + +std::vector > tester; +mailer.push_back(make_pair(3, 6)); +mailer.push_back(make_pair(1, 9)); +mailer.push_back(make_pair(5, 0)); + +// 정렬 함수에 세 번째 인수로 람다 표현식을 전달합니다. +// 정렬은 헤더에 있습니다. + +std::sort(tester.begin(), tester.end(), [](const pair& lhs, const pair& rhs) { + return lhs.second < rhs.second; + }); + +// 람다의 구문을 참고하십시오. +// 람다의 []는 변수를 "캡처"하는 데 사용됩니다. +// "캡처 목록"은 람다 외부에서 함수 본문 내에서 사용할 수 있어야 하는 것과 그 방법을 정의합니다. +// 다음 중 하나일 수 있습니다: +// 1. 값 : [x] +// 2. 참조 : [&x] +// 3. 참조로 현재 범위의 모든 변수 [&] +// 4. 3과 동일하지만 값으로 [=] +// 예: + +std::vector dog_ids; +// number_of_dogs = 3; +for (int i = 0; i < 3; i++) { + dog_ids.push_back(i); +} + +int weight[3] = {30, 50, 10}; + +// 개의 무게에 따라 dog_ids를 정렬하고 싶다고 가정해 보겠습니다. +// 따라서 dog_ids는 결국 [2, 0, 1]이 되어야 합니다. + +// 여기서 람다 표현식이 유용합니다. + +sort(dog_ids.begin(), dog_ids.end(), [&weight](const int &lhs, const int &rhs) { + return weight[lhs] < weight[rhs]; + }); +// 위 예제에서 "weight"를 참조로 캡처했습니다. +// C++의 람다에 대한 자세한 내용: https://stackoverflow.com/questions/7627098/what-is-a-lambda-expression-in-c11 + +/////////////////////////////// +// 범위 For(C++11 이상) +/////////////////////////////// + +// 범위 for 루프를 사용하여 컨테이너를 반복할 수 있습니다. +int arr[] = {1, 10, 3}; + +for (int elem: arr) { + cout << elem << endl; +} + +// "auto"를 사용하고 컨테이너 요소의 유형에 대해 걱정하지 않을 수 있습니다. +// 예를 들어: + +for (auto elem: arr) { + // arr의 각 요소로 무언가를 합니다. +} + +///////////// +// 재미있는 것들 +///////////// + +// 신규 사용자(심지어 일부 베테랑)에게 놀라울 수 있는 C++의 측면. +// 이 섹션은 불행히도 매우 불완전합니다. C++는 발등을 찍기 가장 쉬운 언어 중 하나입니다. + +// private 메서드를 재정의할 수 있습니다! +class Foo { + virtual void bar(); +}; +class FooSub : public Foo { + virtual void bar(); // Foo::bar를 재정의합니다! +}; + + +// 0 == false == NULL (대부분의 경우)! +bool* pt = new bool; +*pt = 0; // 'pt'가 가리키는 값을 false로 설정합니다. +pt = 0; // 'pt'를 null 포인터로 설정합니다. 두 줄 모두 경고 없이 컴파일됩니다. + +// nullptr은 그 문제의 일부를 해결하기 위한 것입니다: +int* pt2 = new int; +*pt2 = nullptr; // 컴파일되지 않음 +pt2 = nullptr; // pt2를 null로 설정합니다. + +// bool에 대한 예외가 있습니다. +// 이것은 if(!ptr)로 null 포인터를 테스트할 수 있도록 하기 위한 것이지만, +// 결과적으로 nullptr을 bool에 직접 할당할 수 있습니다! +*pt = nullptr; // '*pt'가 bool임에도 불구하고 이것은 여전히 컴파일됩니다! + + +// '=' != '=' != '='! +// Foo::Foo(const Foo&) 또는 일부 변형(이동 의미론 참조) 복사 +// 생성자를 호출합니다. +Foo f2; +Foo f1 = f2; + +// Foo::Foo(const Foo&) 또는 변형을 호출하지만 'fooSub'의 'Foo' 부분만 복사합니다. +// 'fooSub'의 추가 멤버는 버려집니다. 이 때로는 +// 끔찍한 동작을 "객체 슬라이싱"이라고 합니다. +FooSub fooSub; +Foo f1 = fooSub; + +// Foo::operator=(Foo&) 또는 변형을 호출합니다. +Foo f1; +f1 = f2; + + +/////////////////////////////////////// +// 튜플(C++11 이상) +/////////////////////////////////////// + +#include + +// 개념적으로 튜플은 오래된 데이터 구조(C와 유사한 구조체)와 유사하지만 +// 명명된 데이터 멤버를 갖는 대신 +// 해당 요소는 튜플에서의 순서로 액세스됩니다. + +// 튜플 생성부터 시작하겠습니다. +// 튜플에 값 패킹 +auto first = make_tuple(10, 'A'); +const int maxN = 1e9; +const int maxL = 15; +auto second = make_tuple(maxN, maxL); + +// 'first' 튜플의 요소 인쇄 +std::cout << get<0>(first) << " " << get<1>(first) << '\n'; // 인쇄: 10 A + +// 'second' 튜플의 요소 인쇄 +std::cout << get<0>(second) << " " << get<1>(second) << '\n'; // 인쇄: 1000000000 15 + +// 변수로 튜플 풀기 + +int first_int; +char first_char; +tie(first_int, first_char) = first; +std::cout << first_int << " " << first_char << '\n'; // 인쇄: 10 A + +// 이렇게 튜플을 만들 수도 있습니다. + +tuple third(11, 'A', 3.14141); +// tuple_size는 튜플의 요소 수를 반환합니다(constexpr로). + +std::cout << tuple_size::value << '\n'; // 인쇄: 3 + +// tuple_cat은 모든 튜플의 요소를 동일한 순서로 연결합니다. + +auto concatenated_tuple = tuple_cat(first, second, third); +// concatenated_tuple은 (10, 'A', 1e9, 15, 11, 'A', 3.14141)이 됩니다. + +std::cout << get<0>(concatenated_tuple) << '\n'; // 인쇄: 10 +std::cout << get<3>(concatenated_tuple) << '\n'; // 인쇄: 15 +std::cout << get<5>(concatenated_tuple) << '\n'; // 인쇄: 'A' + + +/////////////////////////////////// +// 논리 및 비트 연산자 +////////////////////////////////// + +// C++의 대부분의 연산자는 다른 언어와 동일합니다. + +// 논리 연산자 + +// C++는 단락 평가를 사용합니다. 즉, 두 번째 인수는 첫 번째 인수가 표현식의 값을 결정하기에 충분하지 않은 경우에만 실행되거나 평가됩니다. + +true && false // **논리곱**을 수행하여 false를 생성합니다. +true || false // **논리합**을 수행하여 true를 생성합니다. +! true // **논리 부정**을 수행하여 false를 생성합니다. + +// 기호 대신 동등한 키워드를 사용할 수 있습니다. +true and false // **논리곱**을 수행하여 false를 생성합니다. +true or false // **논리합**을 수행하여 true를 생성합니다. +not true // **논리 부정**을 수행하여 false를 생성합니다. + +// 비트 연산자 + +// **<<** 왼쪽 시프트 연산자 +// <<는 비트를 왼쪽으로 시프트합니다. +4 << 1 // 4의 비트를 왼쪽으로 1만큼 시프트하여 8을 생성합니다. +// x << n은 x * 2^n으로 생각할 수 있습니다. + + +// **>>** 오른쪽 시프트 연산자 +// >>는 비트를 오른쪽으로 시프트합니다. +4 >> 1 // 4의 비트를 오른쪽으로 1만큼 시프트하여 2를 생성합니다. +// x >> n은 x / 2^n으로 생각할 수 있습니다. + +~4 // 비트 부정을 수행합니다. +4 | 3 // 비트 또는을 수행합니다. +4 & 3 // 비트 및을 수행합니다. +4 ^ 3 // 비트 배타적 또는을 수행합니다. + +// 동등한 키워드는 다음과 같습니다. +compl 4 // 비트 부정을 수행합니다. +4 bitor 3 // 비트 또는을 수행합니다. +4 bitand 3 // 비트 및을 수행합니다. +4 xor 3 // 비트 배타적 또는을 수행합니다. + +``` \ No newline at end of file diff --git a/ko/c.md b/ko/c.md new file mode 100644 index 0000000000..bd06885bff --- /dev/null +++ b/ko/c.md @@ -0,0 +1,821 @@ +--- +name: C +filename: learnc.c +contributors: + - ["Adam Bard", "http://adambard.com/"] + - ["Árpád Goretity", "http://twitter.com/H2CO3_iOS"] + - ["Jakub Trzebiatowski", "http://cbs.stgn.pl"] + - ["Marco Scannadinari", "https://marcoms.github.io"] + - ["Zachary Ferguson", "https://github.com/zfergus2"] + - ["himanshu", "https://github.com/himanshu81494"] + - ["Joshua Li", "https://github.com/JoshuaRLi"] + - ["Dragos B. Chirila", "https://github.com/dchirila"] + - ["Heitor P. de Bittencourt", "https://github.com/heitorPB/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +아, C. 여전히 현대 고성능 컴퓨팅의 **언어**입니다. + +C는 대부분의 프로그래머가 사용할 가장 낮은 수준의 언어이지만, +원시 속도로 그것을 보완합니다. 수동 메모리 관리에 유의하면 C는 필요한 만큼 멀리 데려다 줄 것입니다. + +```c +// 한 줄 주석은 //로 시작합니다 - C99 이상에서만 사용 가능합니다. + +/* +여러 줄 주석은 이렇습니다. C89에서도 작동합니다. +*/ + +/* +여러 줄 주석은 중첩되지 않습니다 /* 조심하세요 */ // 주석은 이 줄에서 끝납니다... +*/ // ...이 줄이 아닙니다! + +// 상수: #define <키워드> +// 상수는 요구 사항이 아닌 관례상 대문자로 작성됩니다. +#define DAYS_IN_YEAR 365 + +// 열거형 상수는 상수를 선언하는 또 다른 방법입니다. +// 모든 문장은 세미콜론으로 끝나야 합니다. +enum days {SUN, MON, TUE, WED, THU, FRI, SAT}; +// SUN은 0, MON은 1, TUE는 2 등을 얻습니다. + +// 열거형 값도 지정할 수 있습니다. +enum days {SUN = 1, MON, TUE, WED = 99, THU, FRI, SAT}; +// MON은 자동으로 2, TUE는 3 등을 얻습니다. +// WED는 99, THU는 100, FRI는 101 등을 얻습니다. + +// #include로 헤더 가져오기 +#include +#include +#include + +// <꺾쇠 괄호> 사이의 파일 이름은 컴파일러에게 시스템 라이브러리에서 +// 헤더를 찾도록 지시합니다. +// 자신의 헤더의 경우 꺾쇠 괄호 대신 큰따옴표를 사용하고 +// 경로를 제공하십시오: +#include "my_header.h" // 로컬 파일 +#include "../my_lib/my_lib_header.h" // 상대 경로 + +// .h 파일이나 .c 파일 상단에 미리 함수 시그니처를 선언하십시오. +void function_1(); +int function_2(void); + +// 최소한 함수에서 사용하기 전에 '함수 프로토타입'을 선언해야 합니다. +// 일반적으로 프로토타입은 함수 정의 전에 파일 상단에 배치됩니다. +int add_two_ints(int x1, int x2); // 함수 프로토타입 +// `int add_two_ints(int, int);`도 유효하지만(인수 이름을 지정할 필요 없음), +// 더 쉬운 검사를 위해 프로토타입에도 인수 이름을 지정하는 것이 좋습니다. + +// 함수 정의가 해당 함수를 호출하는 다른 함수보다 먼저 나오면 함수 프로토타입은 필요하지 않습니다. 그러나 표준 관행은 항상 함수 프로토타입을 헤더 파일(*.h)에 추가한 다음 해당 파일을 파일 상단에 #include하는 것입니다. 이렇게 하면 함수가 컴파일러가 존재를 알기 전에 호출될 수 있는 문제를 방지하는 동시에 개발자에게 프로젝트의 나머지 부분과 공유할 깨끗한 헤더 파일을 제공합니다. + +// 프로그램의 진입점은 "main"이라는 함수입니다. 반환 유형은 무엇이든 될 수 있지만 대부분의 운영 체제는 오류 코드 처리를 위해 `int`의 반환 유형을 예상합니다. +int main(void) { + // 프로그램 +} + +// 프로그램을 실행하는 데 사용되는 명령줄 인수는 main에도 전달됩니다. +// argc는 인수 수입니다 - 프로그램 이름은 1로 계산됩니다. +// argv는 문자 배열의 배열입니다 - 인수 자체를 포함합니다. +// argv[0] = 프로그램 이름, argv[1] = 첫 번째 인수 등 +int main (int argc, char** argv) +{ + // printf를 사용하여 출력 인쇄, "print formatted"의 경우 + // %d는 정수, \n는 줄 바꿈입니다. + printf("%d\n", 0); // => 0 인쇄 + + // scanf를 사용하여 입력 받기 + // '&'는 입력 값을 저장할 위치를 정의하는 데 사용됩니다. + int input; + scanf("%d", &input); + + /////////////////////////////////////// + // 유형 + /////////////////////////////////////// + + // C99를 준수하지 않는 컴파일러는 변수가 현재 블록 범위의 맨 위에 선언되어야 합니다. + // C99를 준수하는 컴파일러는 값이 사용되는 지점 근처에 선언할 수 있습니다. + // 튜토리얼을 위해 변수는 C99 준수 표준에 따라 동적으로 선언됩니다. + + // int는 일반적으로 4바이트입니다(`sizeof` 연산자를 사용하여 확인). + int x_int = 0; + + // short는 일반적으로 2바이트입니다(`sizeof` 연산자를 사용하여 확인). + short x_short = 0; + + // char는 프로세서에 대해 가장 작은 주소 지정 가능 단위로 정의됩니다. + // 이것은 일반적으로 1바이트이지만 일부 시스템에서는 더 많을 수 있습니다(예: TI의 TMS320의 경우 2바이트). + char x_char = 0; + char y_char = 'y'; // Char 리터럴은 ''로 인용됩니다. + + // long은 종종 4~8바이트입니다. long long은 최소 8바이트가 보장됩니다. + long x_long = 0; + long long x_long_long = 0; + + // float는 일반적으로 32비트 부동 소수점 숫자입니다. + float x_float = 0.0f; // 'f' 접미사는 부동 소수점 리터럴을 나타냅니다. + + // double은 일반적으로 64비트 부동 소수점 숫자입니다. + double x_double = 0.0; // 접미사가 없는 실수 숫자는 double입니다. + + // 정수 유형은 부호가 없을 수 있습니다(0보다 크거나 같음). + unsigned short ux_short; + unsigned int ux_int; + unsigned long long ux_long_long; + + // 작은따옴표 안의 문자는 기계의 문자 집합에 있는 정수입니다. + '0'; // => ASCII 문자 집합에서 48. + 'A'; // => ASCII 문자 집합에서 65. + + // sizeof(T)는 유형 T를 가진 변수의 크기를 바이트 단위로 제공합니다. + // sizeof(obj)는 표현식(변수, 리터럴 등)의 크기를 산출합니다. + printf("%zu\n", sizeof(int)); // => 4 (4바이트 워드가 있는 대부분의 컴퓨터에서) + + // `sizeof` 연산자의 인수가 표현식인 경우 해당 인수는 평가되지 않습니다(VLA(아래 참조) 제외). + // 이 경우 산출하는 값은 컴파일 타임 상수입니다. + int a = 1; + // size_t는 객체의 크기를 나타내는 데 사용되는 최소 2바이트의 부호 없는 정수 유형입니다. + size_t size = sizeof(a++); // a++는 평가되지 않습니다. + printf("sizeof(a++) = %zu where a = %d\n", size, a); + // "sizeof(a++) = 4 where a = 1" 인쇄 (32비트 아키텍처에서) + + // 배열은 구체적인 크기로 초기화해야 합니다. + char my_char_array[20]; // 이 배열은 1 * 20 = 20바이트를 차지합니다. + int my_int_array[20]; // 이 배열은 4 * 20 = 80바이트를 차지합니다. + // (4바이트 워드 가정) + + // 모두 0과 같은 20개의 int 배열을 다음과 같이 초기화할 수 있습니다: + int my_array[20] = {0}; + // 여기서 "{0}" 부분은 "배열 초기화자"라고 합니다. + // 초기화자에 있는 것 이후의 모든 요소(있는 경우)는 0으로 초기화됩니다: + int my_array[5] = {1, 2}; + // 따라서 my_array는 이제 5개의 요소를 가지며, 처음 두 개를 제외한 모든 요소는 0입니다: + // [1, 2, 0, 0, 0] + // 참고: 동일한 줄에서 배열을 초기화하는 경우 배열의 크기를 명시적으로 선언하지 않아도 됩니다: + int my_array[] = {0}; + // 참고: 크기를 선언하지 않을 때 배열의 크기는 초기화자의 요소 수입니다. "{0}"을 사용하면 my_array는 이제 크기가 1입니다: [0] + // 런타임에 배열의 크기를 평가하려면 바이트 크기를 요소 유형의 바이트 크기로 나눕니다: + size_t my_array_size = sizeof(my_array) / sizeof(my_array[0]); + // 경고: 배열이 함수에 전달될 때 원시 포인터로 "다운그레이드"되므로 함수 내에서 잘못된 결과를 생성합니다. 따라서 위의 문은 함수 내에서 잘못된 결과를 생성합니다. + + // 배열 인덱싱은 다른 언어와 같습니다 -- 또는 + // 다른 언어는 C와 같습니다. + my_array[0]; // => 0 + + // 배열은 변경 가능합니다. 그냥 메모리입니다! + my_array[1] = 2; + printf("%d\n", my_array[1]); // => 2 + + // C99(및 C11의 선택적 기능)에서는 가변 길이 배열(VLA)도 선언할 수 있습니다. 이러한 배열의 크기는 컴파일 타임 상수가 아니어도 됩니다: + printf("Enter the array size: "); // 사용자에게 배열 크기 요청 + int array_size; + fscanf(stdin, "%d", &array_size); + int var_length_array[array_size]; // VLA 선언 + printf("sizeof array = %zu\n", sizeof var_length_array); + + // 예: + // > Enter the array size: 10 + // > sizeof array = 40 + + // 문자열은 NULL(0x00) 바이트로 끝나는 문자 배열일 뿐이며, 문자열에서는 특수 문자 '\0'으로 표현됩니다. + // (문자열 리터럴에 NULL 바이트를 포함할 필요는 없습니다. 컴파일러가 배열 끝에 자동으로 삽입합니다.) + char a_string[20] = "This is a string"; + printf("%s\n", a_string); // %s는 문자열 형식을 지정합니다. + + printf("%d\n", a_string[16]); // => 0 + // 즉, 17번째 바이트는 0입니다(18, 19, 20도 마찬가지). + + // 작은따옴표 안의 문자는 문자 리터럴입니다. + // `int` 유형이며 `char`가 아닙니다(역사적인 이유로). + int cha = 'a'; // 괜찮음 + char chb = 'a'; // 괜찮음(int에서 char로의 암시적 변환) + + // 다차원 배열: + int multi_array[2][5] = { + {1, 2, 3, 4, 5}, + {6, 7, 8, 9, 0} + }; + // 요소에 접근: + int array_int = multi_array[0][2]; // => 3 + + /////////////////////////////////////// + // 연산자 + /////////////////////////////////////// + + // 여러 선언에 대한 약어: + int i1 = 1, i2 = 2; + float f1 = 1.0, f2 = 2.0; + + int b, c; + b = c = 0; + + // 산술은 간단합니다. + i1 + i2; // => 3 + i2 - i1; // => 1 + i2 * i1; // => 2 + i1 / i2; // => 0 (0.5이지만 0으로 잘림) + + // 부동 소수점 결과를 얻으려면 최소한 하나의 정수를 float로 캐스팅해야 합니다. + (float)i1 / i2; // => 0.5f + i1 / (double)i2; // => 0.5 // double도 마찬가지 + f1 / f2; // => 0.5, 플러스 또는 마이너스 엡실론 + + // 부동 소수점 숫자는 IEEE 754로 정의되므로 완벽하게 정확한 값을 저장할 수 없습니다. 예를 들어, 다음은 예상 결과를 생성하지 않습니다. 0.1은 실제로 컴퓨터 내부에서 0.099999999999일 수 있고 0.3은 0.300000000001로 저장될 수 있기 때문입니다. + (0.1 + 0.1 + 0.1) != 0.3; // => 1 (참) + // 그리고 위에서 언급한 이유로 결합 법칙이 성립하지 않습니다. + 1 + (1e123 - 1e123) != (1 + 1e123) - 1e123; // => 1 (참) + // 이 표기법은 숫자에 대한 과학적 표기법입니다: 1e123 = 1*10^123 + + // 대부분의 시스템이 부동 소수점을 나타내기 위해 IEEE 754를 사용했다는 점은 주목할 가치가 있습니다. 과학 컴퓨팅에 사용되는 파이썬조차도 결국 IEEE 754를 사용하는 C를 호출합니다. 이것은 이것이 잘못된 구현임을 나타내는 것이 아니라 부동 소수점 비교를 수행할 때 약간의 오류(엡실론)를 고려해야 한다는 경고로 언급됩니다. + + // 모듈로도 있지만 인수가 음수이면 조심하십시오. + 11 % 3; // => 2 as 11 = 2 + 3*x (x=3) + (-11) % 3; // => -2, 예상대로 + 11 % (-3); // => 2이고 -2가 아니며, 상당히 직관적이지 않습니다. + + // 비교 연산자는 아마도 익숙할 것이지만, + // C에는 부울 유형이 없습니다. 대신 int를 사용합니다. + // (C99는 stdbool.h에 제공된 _Bool 유형을 도입했습니다.) + // 0은 거짓이고, 다른 모든 것은 참입니다. (비교 연산자는 항상 0 또는 1을 산출합니다.) + 3 == 2; // => 0 (거짓) + 3 != 2; // => 1 (참) + 3 > 2; // => 1 + 3 < 2; // => 0 + 2 <= 2; // => 1 + 2 >= 2; // => 1 + + // C는 파이썬이 아닙니다 - 비교는 연결되지 않습니다. + // 경고: 아래 줄은 컴파일되지만 `(0 < a) < 2`를 의미합니다. + // 이 표현식은 항상 참입니다. 왜냐하면 (0 < a)는 1 또는 0일 수 있기 때문입니다. + // 이 경우 (0 < 1)이므로 1입니다. + int between_0_and_2 = 0 < a < 2; + // 대신 다음을 사용하십시오: + int between_0_and_2 = 0 < a && a < 2; + + // 논리는 int에서 작동합니다. + !3; // => 0 (논리 부정) + !0; // => 1 + 1 && 1; // => 1 (논리곱) + 0 && 1; // => 0 + 0 || 1; // => 1 (논리합) + 0 || 0; // => 0 + + // 조건부 삼항 표현식 ( ? : ) + int e = 5; + int f = 10; + int z; + z = (e > f) ? e : f; // => 10 "if e > f return e, else return f." + + // 증가 및 감소 연산자: + int j = 0; + int s = j++; // j를 반환한 다음 j를 증가시킵니다. (s = 0, j = 1) + s = ++j; // j를 증가시킨 다음 j를 반환합니다. (s = 2, j = 2) + // j-- 및 --j도 마찬가지입니다. + + // 비트 연산자! + ~0x0F; // => 0xFFFFFFF0 (비트 부정, "1의 보수", 32비트 int에 대한 예제 결과) + 0x0F & 0xF0; // => 0x00 (비트 AND) + 0x0F | 0xF0; // => 0xFF (비트 OR) + 0x04 ^ 0x0F; // => 0x0B (비트 XOR) + 0x01 << 1; // => 0x02 (비트 왼쪽 시프트 (1만큼)) + 0x02 >> 1; // => 0x01 (비트 오른쪽 시프트 (1만큼)) + + // 부호 있는 정수를 시프트할 때 조심하십시오 - 다음은 정의되지 않았습니다: + // - 부호 있는 정수의 부호 비트로 시프트 (int a = 1 << 31) + // - 음수를 왼쪽으로 시프트 (int a = -1 << 2) + // - LHS의 유형 너비보다 크거나 같은 오프셋으로 시프트: + // int a = 1 << 32; // int가 32비트 너비인 경우 UB + + /////////////////////////////////////// + // 제어 구조 + /////////////////////////////////////// + + if (0) { + printf("I am never run\n"); + } else if (0) { + printf("I am also never run\n"); + } else { + printf("I print\n"); + } + + // While 루프가 있습니다. + int ii = 0; + while (ii < 10) { // 10보다 작은 모든 값은 참입니다. + printf("%d, ", ii++); // ii++는 현재 값을 사용한 후 ii를 증가시킵니다. + } // => "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " 인쇄 + + printf("\n"); + + int kk = 0; + do { + printf("%d, ", kk); + } while (++kk < 10); // ++kk는 현재 값을 사용하기 전에 kk를 증가시킵니다. + // => "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " 인쇄 + + printf("\n"); + + // For 루프도 있습니다. + int jj; + for (jj=0; jj < 10; jj++) { + printf("%d, ", jj); + } // => "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, " 인쇄 + + printf("\n"); + + // *****참고*****: + // 루프와 함수는 반드시 본문을 가져야 합니다. 본문이 필요하지 않은 경우: + int i; + for (i = 0; i <= 5; i++) { + ; // 세미콜론을 사용하여 본문 역할을 합니다(null 문). + } + // 또는 + for (i = 0; i <= 5; i++); + + // 여러 선택肢가 있는 분기: switch() + switch (a) { + case 0: // 레이블은 정수 *상수* 표현식이어야 합니다(예: 열거형). + printf("Hey, 'a' equals 0!\n"); + break; // break하지 않으면 제어 흐름이 레이블을 넘어갑니다. + case 1: + printf("Huh, 'a' equals 1!\n"); + break; + // 조심하세요 - "break"가 없으면 다음 "break"에 도달할 때까지 실행이 계속됩니다. + case 3: + case 4: + printf("Look at that.. 'a' is either 3, or 4\n"); + break; + default: + // `some_integral_expression`이 레이블과 일치하지 않는 경우 + fputs("Error!\n", stderr); + exit(-1); + break; + } + /* + C에서 "goto" 사용 + */ + int i, j; + for(i=0; i<100; ++i) + for(j=0; j<100; ++j) + { + if((i + j) >= 150) { + goto error; // 두 for 루프를 즉시 종료합니다. + } + } + printf("No error found. Completed normally.\n"); + goto end; + + error: // 이것은 "goto error;"로 "점프"할 수 있는 레이블입니다. + printf("Error occurred at i = %d & j = %d.\n", i, j); + end: + return 0 + /* + https://ideone.com/z7nzKJ + 이것은 "Error occurred at i = 51 & j = 99."를 인쇄합니다. + */ + /* + 일반적으로 무엇을 하는지 정말로 알지 못하는 한 그렇게 하는 것은 나쁜 관행으로 간주됩니다. 참조 + https://en.wikipedia.org/wiki/Spaghetti_code#Meaning + */ + + /////////////////////////////////////// + // 타입캐스팅 + /////////////////////////////////////// + + // C의 모든 값에는 유형이 있지만, 원하는 경우 한 값을 다른 유형으로 캐스팅할 수 있습니다(일부 제약 조건 있음). + + int x_hex = 0x01; // 16진수 리터럴로 변수를 할당할 수 있습니다. + // 이진수는 표준에 없지만 일부 컴파일러에서 허용됩니다(x_bin = 0b0010010110). + + // 유형 간 캐스팅은 숫자 값을 보존하려고 시도합니다. + printf("%d\n", x_hex); // => 1 인쇄 + printf("%d\n", (short) x_hex); // => 1 인쇄 + printf("%d\n", (char) x_hex); // => 1 인쇄 + + // 유형의 최대값보다 큰 값을 할당하면 경고 없이 롤오버됩니다. + printf("%d\n", (unsigned char) 257); // => 1 (char가 8비트 길이인 경우 최대 char = 255) + + // `char`, `signed char` 및 `unsigned char`의 최대값을 결정하려면 각각 의 CHAR_MAX, SCHAR_MAX 및 UCHAR_MAX 매크로를 사용하십시오. + + // 정수 유형은 부동 소수점 유형으로 캐스팅할 수 있으며 그 반대도 마찬가지입니다. + printf("%f\n", (double) 100); // %f는 항상 double 형식을 지정합니다... + printf("%f\n", (float) 100); // ...float도 마찬가지입니다. + printf("%d\n", (char)100.0); + + /////////////////////////////////////// + // 포인터 + /////////////////////////////////////// + + // 포인터는 메모리 주소를 저장하도록 선언된 변수입니다. 해당 선언은 가리키는 데이터 유형도 알려줍니다. 변수의 메모리 주소를 검색한 다음 조작할 수 있습니다. + + int x = 0; + printf("%p\n", (void *)&x); // &를 사용하여 변수의 주소를 검색합니다. + // (%p는 void * 유형의 객체 포인터 형식을 지정합니다.) + // => 메모리의 일부 주소를 인쇄합니다. + + // 포인터는 선언에서 *로 시작합니다. + int *px, not_a_pointer; // px는 int에 대한 포인터입니다. + px = &x; // x의 주소를 px에 저장합니다. + printf("%p\n", (void *)px); // => 메모리의 일부 주소를 인쇄합니다. + printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer)); + // => 일반적인 64비트 시스템에서 "8, 4"를 인쇄합니다. + + // 포인터가 가리키는 주소의 값을 검색하려면 앞에 *를 붙여 역참조합니다. + // 참고: 예, *가 포인터를 선언하고 역참조하는 데 모두 사용되어 혼란스러울 수 있습니다. + printf("%d\n", *px); // => x의 값인 0을 인쇄합니다. + + // 포인터가 가리키는 값을 변경할 수도 있습니다. + // ++가 *보다 우선 순위가 높기 때문에 역참조를 괄호로 묶어야 합니다. + (*px)++; // px가 가리키는 값을 1만큼 증가시킵니다. + printf("%d\n", *px); // => 1을 인쇄합니다. + printf("%d\n", x); // => 1을 인쇄합니다. + + // 배열은 연속적인 메모리 블록을 할당하는 좋은 방법입니다. + int x_array[20]; // 크기 20의 배열을 선언합니다(크기 변경 불가). + int xx; + for (xx = 0; xx < 20; xx++) { + x_array[xx] = 20 - xx; + } // x_array를 20, 19, 18,... 2, 1로 초기화합니다. + + // int 유형의 포인터를 선언하고 x_array를 가리키도록 초기화합니다. + int* x_ptr = x_array; + // x_ptr은 이제 배열의 첫 번째 요소(정수 20)를 가리킵니다. + // 이것은 배열이 종종 첫 번째 요소에 대한 포인터로 붕괴되기 때문에 작동합니다. + // 예를 들어, 배열이 함수에 전달되거나 포인터에 할당될 때 포인터로 붕괴(암시적으로 변환)됩니다. + // 예외: 배열이 `&` (주소) 연산자의 인수인 경우: + int arr[10]; + int (*ptr_to_arr)[10] = &arr; // &arr은 `int *` 유형이 아닙니다! + // "포인터 대 배열"(10개의 `int`로 구성) 유형입니다. + // 또는 배열이 char 배열을 초기화하는 데 사용되는 문자열 리터럴인 경우: + char otherarr[] = "foobarbazquirk"; + // 또는 `sizeof` 또는 `alignof` 연산자의 인수인 경우: + int arraythethird[10]; + int *ptr = arraythethird; // int *ptr = &arr[0]과 동일합니다. + printf("%zu, %zu\n", sizeof(arraythethird), sizeof(ptr)); + // 아마도 "40, 4" 또는 "40, 8"을 인쇄합니다. + + // 포인터는 유형에 따라 증가 및 감소됩니다. + // (이것을 포인터 산술이라고 합니다.) + printf("%d\n", *(x_ptr + 1)); // => 19 인쇄 + printf("%d\n", x_array[1]); // => 19 인쇄 + + // 표준 라이브러리 함수 malloc을 사용하여 연속적인 메모리 블록을 동적으로 할당할 수도 있습니다. 이 함수는 할당할 바이트 수를 나타내는 size_t 유형의 인수 하나를 사용합니다(일반적으로 힙에서, 그러나 예를 들어 임베디드 시스템에서는 그렇지 않을 수 있습니다 - C 표준은 이에 대해 아무것도 말하지 않습니다). + int *my_ptr = malloc(sizeof(*my_ptr) * 20); + for (xx = 0; xx < 20; xx++) { + *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx + } // 메모리를 20, 19, 18, 17... 2, 1(int로)로 초기화합니다. + + // 사용자 제공 값을 malloc에 전달할 때 조심하십시오! 안전을 원한다면 대신 calloc을 사용할 수 있습니다(malloc과 달리 메모리를 0으로 채웁니다). + int* my_other_ptr = calloc(20, sizeof(int)); + + // C에서 동적으로 할당된 배열의 길이를 얻는 표준적인 방법은 없습니다. 이 때문에 배열이 프로그램 전체에서 많이 전달되는 경우 배열의 요소 수(크기)를 추적하기 위해 다른 변수가 필요합니다. 자세한 내용은 함수 섹션을 참조하십시오. + size_t size = 10; + int *my_arr = calloc(size, sizeof(int)); + // 배열에 요소 추가 + size++; + my_arr = realloc(my_arr, sizeof(int) * size); + if (my_arr == NULL) { + // realloc 실패를 확인하는 것을 잊지 마십시오! + return + } + my_arr[10] = 5; + + // 할당하지 않은 메모리를 역참조하면 + // "예측할 수 없는 결과"가 발생합니다 - 프로그램이 "정의되지 않은 동작"을 호출한다고 합니다. + printf("%d\n", *(my_ptr + 21)); // => 누가 알겠습니까? 충돌할 수도 있습니다. + + // malloc으로 할당된 메모리 블록을 다 사용하면 해제해야 합니다. + // 그렇지 않으면 프로그램이 종료될 때까지 다른 누구도 사용할 수 없습니다. + // (이것을 "메모리 누수"라고 합니다): + free(my_ptr); + + // 문자열은 문자 배열이지만 일반적으로 문자 포인터(배열의 첫 번째 요소에 대한 포인터)로 표현됩니다. + // 문자열 리터럴은 수정해서는 안 되므로 문자열 리터럴을 참조할 때 `const char *`를 사용하는 것이 좋습니다(즉, "foo"[0] = 'a'는 불법입니다). + const char *my_str = "This is my very own string literal"; + printf("%c\n", *my_str); // => 'T' + + // 문자열이 쓰기 가능한 메모리에 있는 배열인 경우(문자열 리터럴로 초기화될 수 있음)는 그렇지 않습니다. 예: + char foo[] = "foo"; + foo[0] = 'a'; // 이것은 합법적이며, foo는 이제 "aoo"를 포함합니다. + + function_1(); +} // 주 함수 끝 + +/////////////////////////////////////// +// 함수 +/////////////////////////////////////// + +// 함수 선언 구문: +// <반환 유형> <함수 이름>(<인수>) + +int add_two_ints(int x1, int x2) +{ + return x1 + x2; // return을 사용하여 값을 반환합니다. +} + +/* +함수는 값에 의한 호출입니다. 함수가 호출될 때 함수에 전달된 인수는 원래 인수의 복사본입니다(배열 제외). 함수에서 인수에 대해 수행하는 모든 작업은 함수가 호출된 원래 인수의 값을 변경하지 않습니다. + +원래 인수 값을 편집해야 하는 경우 포인터를 사용하십시오(배열은 항상 포인터로 전달됩니다). + +예: 제자리 문자열 반전 +*/ + +// void 함수는 값을 반환하지 않습니다. +void str_reverse(char *str_in) +{ + char tmp; + size_t ii = 0; + size_t len = strlen(str_in); // `strlen()`은 c 표준 라이브러리의 일부입니다. + // 참고: `strlen`에서 반환된 길이는 종료 NULL 바이트('\0')를 포함하지 않습니다. + // c99 및 최신 버전에서는 루프의 괄호 안에 루프 제어 변수를 직접 선언할 수 있습니다. 예: `for (size_t ii = 0; ...` + for (ii = 0; ii < len / 2; ii++) { + tmp = str_in[ii]; + str_in[ii] = str_in[len - ii - 1]; // 끝에서 ii번째 문자 + str_in[len - ii - 1] = tmp; + } +} +// 참고: strlen()을 사용하려면 string.h 헤더 파일을 포함해야 합니다. + +/* +char c[] = "This is a test."; +str_reverse(c); +printf("%s\n", c); // => ".tset a si sihT" +*/ +/* +하나의 변수만 반환할 수 있으므로 +둘 이상의 변수 값을 변경하려면 참조에 의한 호출을 사용합니다. +*/ +void swapTwoNumbers(int *a, int *b) +{ + int temp = *a; + *a = *b; + *b = temp; +} +/* +int first = 10; +int second = 20; +printf("first: %d\nsecond: %d\n", first, second); +swapTwoNumbers(&first, &second); +printf("first: %d\nsecond: %d\n", first, second); +// 값이 교환됩니다. +*/ + +// 여러 값 반환. +// C는 return 문으로 여러 값을 반환하는 것을 허용하지 않습니다. 여러 값을 반환하려면 호출자가 반환된 값을 원하는 변수를 전달해야 합니다. 이러한 변수는 함수가 수정할 수 있도록 포인터로 전달해야 합니다. +int return_multiple( int *array_of_3, int *ret1, int *ret2, int *ret3) +{ + if(array_of_3 == NULL) + return 0; // 오류 코드 반환 (거짓) + + // 포인터를 역참조하여 값을 수정합니다. + *ret1 = array_of_3[0]; + *ret2 = array_of_3[1]; + *ret3 = array_of_3[2]; + + return 1; // 오류 코드 반환 (참) +} + +/* +배열과 관련하여, 배열은 항상 포인터로 함수에 전달됩니다. `arr[10]`과 같이 정적으로 배열을 할당하더라도 함수 호출에서 첫 번째 요소에 대한 포인터로 전달됩니다. +다시 말하지만, C에서 동적으로 할당된 배열의 크기를 얻는 표준적인 방법은 없습니다. +*/ +// 크기를 전달해야 합니다! +// 그렇지 않으면 이 함수는 배열이 얼마나 큰지 알 수 없습니다. +void printIntArray(int *arr, size_t size) { + int i; + for (i = 0; i < size; i++) { + printf("arr[%d] is: %d\n", i, arr[i]); + } +} +/* +int my_arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; +int size = 10; +printIntArray(my_arr, size); +// "arr[0] is: 1" 등을 인쇄합니다. +*/ + +// 함수 외부의 외부 변수를 참조하는 경우 extern 키워드를 사용해야 합니다. +int i = 0; +void testFunc() { + extern int i; // 여기서 i는 이제 외부 변수 i를 사용합니다. +} + +// static을 사용하여 외부 변수를 소스 파일에 비공개로 만듭니다: +static int j = 0; // testFunc2()를 사용하는 다른 파일은 변수 j에 액세스할 수 없습니다. +void testFunc2() { + extern int j; +} +// static 키워드는 변수를 컴파일 단위 외부의 코드에서 액세스할 수 없게 만듭니다. (거의 모든 시스템에서 "컴파일 단위"는 .c 파일입니다.) static은 전역(컴파일 단위에 대한) 변수, 함수 및 함수 로컬 변수 모두에 적용할 수 있습니다. +// **함수를 static으로 선언하여 비공개로 만들 수도 있습니다.** + +/////////////////////////////////////// +// 사용자 정의 유형 및 구조체 +/////////////////////////////////////// + +// Typedef를 사용하여 유형 별칭을 만들 수 있습니다. +typedef int my_type; +my_type my_type_var = 0; + +// 구조체는 데이터 모음일 뿐이며, 멤버는 작성된 순서대로 순차적으로 할당됩니다: +struct rectangle { + int width; + int height; +}; + +// 일반적으로 +// sizeof(struct rectangle) == sizeof(int) + sizeof(int)는 아닙니다. +// 구조체 멤버 간의 잠재적인 패딩 때문입니다(정렬 이유로). [1] + +void function_1() +{ + struct rectangle my_rec = { 1, 2 }; // 필드는 즉시 초기화할 수 있습니다. + + // .으로 구조체 멤버에 액세스합니다. + my_rec.width = 10; + my_rec.height = 20; + + // 구조체에 대한 포인터를 선언할 수 있습니다. + struct rectangle *my_rec_ptr = &my_rec; + + // 역참조를 사용하여 구조체 포인터 멤버를 설정합니다... + (*my_rec_ptr).width = 30; + + // ... 또는 가독성을 위해 -> 약어를 선호합니다. + my_rec_ptr->height = 10; // (*my_rec_ptr).height = 10;과 동일합니다. +} + +// 편의를 위해 구조체에 typedef를 적용할 수 있습니다. +typedef struct rectangle rect; + +int area(rect r) +{ + return r.width * r.height; +} + +// Typedef는 구조체 정의 중에 바로 정의할 수도 있습니다. +typedef struct { + int width; + int height; +} rect; +// 이전과 마찬가지로 이렇게 하면 +rect r; +// 다음과 같이 입력하는 대신 +struct rectangle r; + +// 큰 구조체가 있는 경우 복사를 피하기 위해 "포인터로" 전달할 수 있습니다. +// 전체 구조체: +int areaptr(const rect *r) +{ + return r->width * r->height; +} + +/////////////////////////////////////// +// 함수 포인터 +/////////////////////////////////////// +/* +런타임에 함수는 알려진 메모리 주소에 있습니다. 함수 포인터는 다른 포인터와 매우 유사하지만(메모리 주소만 저장함) 함수를 직접 호출하고 핸들러(또는 콜백 함수)를 전달하는 데 사용할 수 있습니다. 그러나 정의 구문은 처음에는 혼란스러울 수 있습니다. + +예: 포인터에서 str_reverse 사용 +*/ +void str_reverse_through_pointer(char *str_in) { + // f라는 이름의 함수 포인터 변수를 정의합니다. + void (*f)(char *); // 시그니처는 대상 함수와 정확히 일치해야 합니다. + f = &str_reverse; // 실제 함수에 대한 주소를 할당합니다(런타임에 결정됨). + // f = str_reverse;도 작동합니다 - 함수는 배열과 유사하게 포인터로 붕괴됩니다. + (*f)(str_in); // 포인터를 통해 함수를 호출하기만 하면 됩니다. + // f(str_in); // 이것은 호출하기 위한 대안이지만 똑같이 유효한 구문입니다. +} + +/* +함수 시그니처가 일치하는 한 동일한 포인터에 모든 함수를 할당할 수 있습니다. +함수 포인터는 일반적으로 다음과 같이 단순성과 가독성을 위해 typedef됩니다: +*/ + +typedef void (*my_fnp_type)(char *); + +// 그런 다음 실제 포인터 변수를 선언할 때 사용됩니다: +// ... +// my_fnp_type f; + + +///////////////////////////// +// printf()로 문자 인쇄 +///////////////////////////// + +// 특수 문자: +/* +'\a'; // 경고(벨) 문자 +'\n'; // 줄 바꿈 문자 +'\t'; // 탭 문자(텍스트 왼쪽 정렬) +'\v'; // 수직 탭 +'\f'; // 새 페이지(폼 피드) +'\r'; // 캐리지 리턴 +'\b'; // 백스페이스 문자 +'\0'; // NULL 문자. 일반적으로 C의 문자열 끝에 넣습니다. +// hello\n\0. \0은 문자열 끝을 표시하는 관례로 사용됩니다. +'\\' ; // 백슬래시 +'\?'; // 물음표 +'\''; // 작은따옴표 +'\"'; // 큰따옴표 +'\xhh'; // 16진수. 예: '\xb' = 수직 탭 문자 +'\0oo'; // 8진수. 예: '\013' = 수직 탭 문자 + +// 인쇄 형식: +"%d"; // 정수 +"%3d"; // 최소 길이 3자리 정수(텍스트 오른쪽 정렬) +"%s"; // 문자열 +"%f"; // float +"%ld"; // long +"%3.2f"; // 최소 3자리 왼쪽 및 2자리 오른쪽 소수 float +"%7.4s"; // (문자열로도 가능) +"%c"; // char +"%p"; // 포인터. 참고: `printf`에 인수로 전달하기 전에 포인터를 (void *)로 캐스팅해야 합니다. +"%x"; // 16진수 +"%o"; // 8진수 +"%%"; // % 인쇄 +*/ + +/////////////////////////////////////// +// 평가 순서 +/////////////////////////////////////// + +// 위에서 아래로, 위가 우선 순위가 높습니다. +//---------------------------------------------------// +// 연산자 | 결합성 // +//---------------------------------------------------// +// () [] -> . | 왼쪽에서 오른쪽으로 // +// ! ~ ++ -- + - *(type) sizeof | 오른쪽에서 왼쪽으로 // +// * / % | 왼쪽에서 오른쪽으로 // +// + - | 왼쪽에서 오른쪽으로 // +// << >> | 왼쪽에서 오른쪽으로 // +// < <= > >= | 왼쪽에서 오른쪽으로 // +// == != | 왼쪽에서 오른쪽으로 // +// & + | 왼쪽에서 오른쪽으로 // +// ^ | 왼쪽에서 오른쪽으로 // +// | + | 왼쪽에서 오른쪽으로 // +// && | 왼쪽에서 오른쪽으로 // +// || | 왼쪽에서 오른쪽으로 // +// ?: | 오른쪽에서 왼쪽으로 // +// = += -= *= /= %= &= ^= |= <<= >>= | 오른쪽에서 왼쪽으로 // +// , + | 왼쪽에서 오른쪽으로 // +//---------------------------------------------------// + +/******************************* 헤더 파일 ********************************** + +헤더 파일은 C 소스 파일을 연결하고 코드를 단순화하고 정의를 별도의 파일로 분리할 수 있으므로 C의 중요한 부분입니다. + +헤더 파일은 구문적으로 C 소스 파일과 유사하지만 ".h" 파일에 있습니다. C 소스 파일에 #include "example.h"를 사용하여 포함할 수 있습니다. 단, example.h가 C 파일과 동일한 디렉토리에 있어야 합니다. +*/ + +/* 헤더가 너무 많이 정의되는 것을 방지하기 위한 안전 장치입니다. 이것은 */ +/* 원형 종속성의 경우에 발생하며, 헤더의 내용은 */ +/* 이미 정의되어 있습니다. */ +#ifndef EXAMPLE_H /*EXAMPLE_H가 아직 정의되지 않은 경우. */ +#define EXAMPLE_H /*매크로 EXAMPLE_H를 정의합니다. */ + +/* 다른 헤더는 헤더에 포함될 수 있으므로 이 헤더를 포함하는 파일에 */ +/* 전이적으로 포함될 수 있습니다. */ +#include + +/* c 소스 파일과 마찬가지로 매크로는 헤더에 정의할 수 있습니다. */ +/* 이 헤더 파일을 포함하는 파일에서 사용됩니다. */ +#define EXAMPLE_NAME "Dennis Ritchie" + +/* 함수 매크로도 정의할 수 있습니다. */ +#define ADD(a, b) ((a) + (b)) + +/* 인수를 둘러싼 괄호에 유의하십시오 -- 이것은 a와 b가 예기치 않은 방식으로 확장되지 않도록 하는 데 중요합니다(예: MUL(x, y) (x * y)를 고려하십시오. MUL(1 + 2, 3)은 (1 + 2 * 3)으로 확장되어 잘못된 결과를 산출합니다). */ + +/* 구조체 및 typedef는 파일 간의 일관성을 위해 사용할 수 있습니다. */ +typedef struct Node +{ + int val; + struct Node *next; +} Node; + +/* 열거형도 마찬가지입니다. */ +enum traffic_light_state {GREEN, YELLOW, RED}; + +/* 함수 프로토타입은 여러 파일에서 사용하기 위해 여기에서 정의할 수도 있지만, 헤더에서 함수를 정의하는 것은 나쁜 관행입니다. 정의는 대신 C 파일에 넣어야 합니다. */ +Node createLinkedList(int *vals, int len); + +/* 위의 요소 외에 다른 정의는 C 소스 파일에 남겨두어야 합니다. 과도한 포함 또는 정의도 헤더 파일에 포함되어서는 안 되며 대신 별도의 헤더 또는 C 파일에 넣어야 합니다. */ + +#endif /* if 전처리기 지시문의 끝입니다. */ +``` + +## 더 읽을거리 + +[K&R, 일명 "The C Programming Language"](https://en.wikipedia.org/wiki/The_C_Programming_Language) 사본을 찾는 것이 가장 좋습니다. C의 창시자인 Dennis Ritchie와 Brian Kernighan이 쓴 C에 대한 _책_입니다. 그러나 조심하십시오 - 오래되었고 일부 부정확한 내용(음, 더 이상 좋다고 생각되지 않는 아이디어) 또는 현재 변경된 관행이 포함되어 있습니다. + +또 다른 좋은 자료는 [Learn C The Hard Way](http://learncodethehardway.org/c/)입니다(무료 아님). + +질문이 있는 경우 [compl.lang.c 자주 묻는 질문](http://c-faq.com)을 읽어보십시오. + +일반적으로 적절한 간격, 들여쓰기를 사용하고 코딩 스타일을 일관되게 유지하는 것이 매우 중요합니다. +읽기 쉬운 코드는 영리한 코드와 빠른 코드보다 낫습니다. 채택할 좋은, 건전한 코딩 스타일에 대해서는 [Linux 커널 코딩 스타일](https://www.kernel.org/doc/Documentation/process/coding-style.rst)을 참조하십시오. + +[1] [구조체의 sizeof가 각 멤버의 sizeof의 합과 같지 않은 이유는 무엇입니까?](https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member) \ No newline at end of file diff --git a/ko/chapel.md b/ko/chapel.md new file mode 100644 index 0000000000..7b54b38b3a --- /dev/null +++ b/ko/chapel.md @@ -0,0 +1,1430 @@ +--- +name: Chapel +filename: learnchapel.chpl +contributors: + - ["Ian J. Bertolacci", "https://www.cs.arizona.edu/~ianbertolacci/"] + - ["Ben Harshbarger", "https://github.com/benharsh/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +[Cray의 공식 Chapel 웹사이트](https://chapel-lang.org)에서 Chapel에 대한 모든 것을 읽을 수 있습니다. +간단히 말해, Chapel은 Cray Inc.에서 개발 중인 오픈 소스, 고생산성, 병렬 프로그래밍 +언어이며, 다중 코어 PC뿐만 아니라 다중 킬로코어 슈퍼컴퓨터에서도 실행되도록 설계되었습니다. + +더 많은 정보와 지원은 이 문서 하단에서 찾을 수 있습니다. + +이 문서의 [최신 버전](https://chapel-lang.org/docs/master/primers/learnChapelInYMinutes.html)은 공식 사이트를 참조할 수 있습니다. + +```chapel +/* + Y분 만에 Chapel 배우기 + + 이 입문서는 Chapel의 기본 구문과 개념을 다룹니다. + 공식 페이지와 마지막 동기화: 2020년 3월 8일 일요일 08:05:53 +0000 +*/ + +// 주석은 C 계열 스타일입니다. + +// 한 줄 주석 +/* + 여러 줄 주석 +*/ + +/* +기본 인쇄 +*/ + +write("Hello, "); +writeln("World!"); + +// ``write``와 ``writeln``은 인쇄할 항목 목록을 사용할 수 있습니다. +// 각 항목은 다른 항목 바로 옆에 인쇄되므로 간격을 포함하십시오! +writeln("There are ", 3, " commas (",") in this line of code"); + +// 다른 출력 채널: +use IO; // 다른 출력 채널에 액세스하려면 필요합니다. + +stdout.writeln("This goes to standard output, just like plain writeln() does"); +stderr.writeln("This goes to standard error"); + +/* +변수 +*/ + +// 변수는 컴파일러가 보유할 유형을 파악할 수 있는 한 +// 명시적으로 유형을 지정할 필요가 없습니다. +// 10은 ``int``이므로 ``myVar``는 암시적으로 ``int``입니다. +var myVar = 10; +myVar = -10; +var mySecondVar = myVar; +// ``var anError;``는 컴파일 타임 오류입니다. + +// 명시적으로 유형을 지정할 수 있습니다(그리고 그래야 합니다). +var myThirdVar: real; +var myFourthVar: real = -1.234; +myThirdVar = myFourthVar; + +/* +유형 +*/ + +// 여러 가지 기본 유형이 있습니다. +var myInt: int = -1000; // 부호 있는 정수 +var myUint: uint = 1234; // 부호 없는 정수 +var myReal: real = 9.876; // 부동 소수점 숫자 +var myImag: imag = 5.0i; // 허수 +var myCplx: complex = 10 + 9i; // 복소수 +myCplx = myInt + myImag; // 복소수를 형성하는 또 다른 방법 +var myBool: bool = false; // 부울 +var myStr: string = "Some string..."; // 문자열 +var singleQuoteStr = 'Another string...'; // 작은따옴표가 있는 문자열 리터럴 + +// 일부 유형은 크기를 가질 수 있습니다. +var my8Int: int(8) = 10; // 8비트(1바이트) 크기 정수; +var my64Real: real(64) = 1.516; // 64비트(8바이트) 크기 실수 + +// 유형 변환. +var intFromReal = myReal : int; +var intFromReal2: int = myReal : int; + +// 유형 별칭. +type chroma = int; // 단일 색조의 유형 +type RGBColor = 3*chroma; // 전체 색상을 나타내는 유형 +var black: RGBColor = (0,0,0); +var white: RGBColor = (255, 255, 255); + +/* +상수 및 매개변수 +*/ + +// ``const``는 상수이며 런타임에 설정된 후에는 변경할 수 없습니다. +const almostPi: real = 22.0/7.0; + +// ``param``은 컴파일 타임에 값을 알아야 하는 상수입니다. +param compileTimeConst: int = 16; + +// ``config`` 수정자는 명령줄에서 값을 설정할 수 있도록 합니다. +// 런타임에 ``--varCmdLineArg=Value`` 또는 ``--varCmdLineArg Value``로 설정합니다. +config var varCmdLineArg: int = -123; +config const constCmdLineArg: int = 777; + +// ``config param``은 컴파일 타임에 설정할 수 있습니다. +// 컴파일 타임에 ``--set paramCmdLineArg=value``로 설정합니다. +config param paramCmdLineArg: bool = false; +writeln(varCmdLineArg, ", ", constCmdLineArg, ", ", paramCmdLineArg); + +/* +참조 +*/ + +// ``ref``는 C++의 참조와 매우 유사하게 작동합니다. Chapel에서 ``ref``는 초기화된 변수 이외의 변수를 별칭으로 만들 수 없습니다. +// 여기서 ``refToActual``은 ``actual``을 참조합니다. +var actual = 10; +ref refToActual = actual; +writeln(actual, " == ", refToActual); // 동일한 값 인쇄 +actual = -123; // actual 수정(refToActual이 참조하는) +writeln(actual, " == ", refToActual); // 동일한 값 인쇄 +refToActual = 99999999; // refToActual이 참조하는 것 수정(actual임) +writeln(actual, " == ", refToActual); // 동일한 값 인쇄 + +/* +연산자 +*/ + +// 수학 연산자: +var a: int, thisInt = 1234, thatInt = 5678; +a = thisInt + thatInt; // 덧셈 +a = thisInt * thatInt; // 곱셈 +a = thisInt - thatInt; // 뺄셈 +a = thisInt / thatInt; // 나눗셈 +a = thisInt ** thatInt; // 거듭제곱 +a = thisInt % thatInt; // 나머지 (모듈로) + +// 논리 연산자: +var b: bool, thisBool = false, thatBool = true; +b = thisBool && thatBool; // 논리곱 +b = thisBool || thatBool; // 논리합 +b = !thisBool; // 논리 부정 + +// 관계 연산자: +b = thisInt > thatInt; // 보다 큼 +b = thisInt >= thatInt; // 보다 크거나 같음 +b = thisInt < a && a <= thatInt; // 보다 작음, 그리고, 보다 작거나 같음 +b = thisInt != thatInt; // 같지 않음 +b = thisInt == thatInt; // 같음 + +// 비트 연산자: +a = thisInt << 10; // 왼쪽으로 10비트 시프트; +a = thatInt >> 5; // 오른쪽으로 5비트 시프트; +a = ~thisInt; // 비트 부정 +a = thisInt ^ thatInt; // 비트 배타적 논리합 + +// 복합 할당 연산자: +a += thisInt; // 덧셈-등호 (a = a + thisInt;) +a *= thatInt; // 곱셈-등호 (a = a * thatInt;) +b &&= thatBool; // 논리곱-등호 (b = b && thatBool;) +a <<= 3; // 왼쪽 비트 시프트-등호 (a = a << 10;) + +// 다른 C 계열 언어와 달리 다음과 같은 전/후 증감 연산자는 없습니다: +// +// ``++j``, ``--j``, ``j++``, ``j--`` + +// 교환 연산자: +var old_this = thisInt; +var old_that = thatInt; +thisInt <=> thatInt; // thisInt와 thatInt의 값을 교환합니다. +writeln((old_this == thatInt) && (old_that == thisInt)); + +// 연산자 오버로드는 프로시저에서 볼 수 있듯이 정의할 수도 있습니다. + +/* +튜플 +*/ + +// 튜플은 동일한 유형이거나 다른 유형일 수 있습니다. +var sameTup: 2*int = (10, -1); +var sameTup2 = (11, -6); +var diffTup: (int,real,complex) = (5, 1.928, myCplx); +var diffTupe2 = (7, 5.64, 6.0+1.5i); + +// 튜플은 대괄호나 괄호를 사용하여 액세스할 수 있으며 1-인덱싱됩니다. +writeln("(", sameTup[1], ",", sameTup(2), ")"); +writeln(diffTup); + +// 튜플에 쓸 수도 있습니다. +diffTup(1) = -1; + +// 튜플 값은 자체 변수로 확장될 수 있습니다. +var (tupInt, tupReal, tupCplx) = diffTup; +writeln(diffTup == (tupInt, tupReal, tupCplx)); + +// 디버깅에 일반적인 변수 목록을 작성하는 데에도 유용합니다. +writeln((a,b,thisInt,thatInt,thisBool,thatBool)); + +/* +제어 흐름 +*/ + +// ``if`` - ``then`` - ``else``는 다른 C 계열 언어와 마찬가지로 작동합니다. +if 10 < 100 then + writeln("All is well"); + +if -1 < 1 then + writeln("Continuing to believe reality"); +else + writeln("Send mathematician, something's wrong"); + +// 원한다면 괄호를 사용할 수 있습니다. +if (10 > 100) { + writeln("Universe broken. Please reboot universe."); +} + +if a % 2 == 0 { + writeln(a, " is even."); +} else { + writeln(a, " is odd."); +} + +if a % 3 == 0 { + writeln(a, " is even divisible by 3."); +} else if a % 3 == 1 { + writeln(a, " is divided by 3 with a remainder of 1."); +} else { + writeln(b, " is divided by 3 with a remainder of 2."); +} + +// 삼항: 문에서 ``if`` - ``then`` - ``else``. +var maximum = if thisInt < thatInt then thatInt else thisInt; + +// ``select`` 문은 다른 언어의 switch 문과 매우 유사합니다. +// 그러나 ``select`` 문은 C나 Java처럼 계단식으로 작동하지 않습니다. +var inputOption = "anOption"; +select inputOption { + when "anOption" do writeln("Chose 'anOption'"); + when "otherOption" { + writeln("Chose 'otherOption'"); + writeln("Which has a body"); + } + otherwise { + writeln("Any other Input"); + writeln("the otherwise case doesn't need a do if the body is one line"); + } +} + +// ``while`` 및 ``do``-``while`` 루프도 C 대응 항목처럼 동작합니다. +var j: int = 1; +var jSum: int = 0; +while (j <= 1000) { + jSum += j; + j += 1; +} +writeln(jSum); + +do { + jSum += j; + j += 1; +} while (j <= 10000); +writeln(jSum); + +// ``for`` 루프는 파이썬의 루프와 매우 유사하며 범위를 반복합니다. 아래의 ``1..10`` 표현식과 같은 범위는 Chapel에서 일급 객체이며 변수에 저장할 수 있습니다. +for i in 1..10 do write(i, ", "); +writeln(); + +var iSum: int = 0; +for i in 1..1000 { + iSum += i; +} +writeln(iSum); + +for x in 1..10 { + for y in 1..10 { + write((x,y), "\t"); + } + writeln(); +} + +/* +범위 및 도메인 +*/ + +// For-루프와 배열은 모두 반복할 수 있는 인덱스 집합을 정의하기 위해 범위와 도메인을 사용합니다. 범위는 단일 차원 정수 인덱스이고, 도메인은 다차원일 수 있으며 다른 유형의 인덱스를 나타낼 수 있습니다. + +// 이들은 일급 시민 유형이며 변수에 할당할 수 있습니다. +var range1to10: range = 1..10; // 1, 2, 3, ..., 10 +var range2to11 = 2..11; // 2, 3, 4, ..., 11 +var rangeThisToThat: range = thisInt..thatInt; // 변수 사용 +var rangeEmpty: range = 100..-100; // 이것은 유효하지만 인덱스를 포함하지 않습니다. + +// 범위는 무한할 수 있습니다. +var range1toInf: range(boundedType=BoundedRangeType.boundedLow) = 1.. ; // 1, 2, 3, 4, 5, ... +var rangeNegInfTo1 = ..1; // ..., -4, -3, -2, -1, 0, 1 + +// 범위는 ``by`` 연산자를 사용하여 보폭을 지정(및 반전)할 수 있습니다. +var range2to10by2: range(stridable=true) = 2..10 by 2; // 2, 4, 6, 8, 10 +var reverse2to10by2 = 2..10 by -2; // 10, 8, 6, 4, 2 + +var trapRange = 10..1 by -1; // 속지 마십시오. 이것은 여전히 빈 범위입니다. +writeln("Size of range ", trapRange, " = ", trapRange.size); + +// 참고: ``range(boundedType= ...)`` 및 ``range(stridable= ...)``는 변수를 명시적으로 입력하는 경우에만 필요합니다. + +// 범위의 끝점은 카운트(``#``) 연산자를 사용하여 범위의 총 크기를 지정하여 계산할 수 있습니다. +var rangeCount: range = -5..#12; // -5에서 6까지의 범위 + +// 연산자를 혼합할 수 있습니다. +var rangeCountBy: range(stridable=true) = -5..#12 by 2; // -5, -3, -1, 1, 3, 5 +writeln(rangeCountBy); + +// 범위의 속성을 쿼리할 수 있습니다. +// 이 예에서는 첫 번째 인덱스, 마지막 인덱스, 인덱스 수, 보폭 및 2가 범위에 포함되어 있는지 여부를 인쇄합니다. +writeln((rangeCountBy.first, rangeCountBy.last, rangeCountBy.size, + rangeCountBy.stride, rangeCountBy.contains(2))); + +for i in rangeCountBy { + write(i, if i == rangeCountBy.last then "\n" else ", "); +} + +// 직사각형 도메인은 동일한 범위 구문을 사용하여 정의되지만 범위와 달리 경계가 있어야 합니다. +var domain1to10: domain(1) = {1..10}; // 1..10의 1D 도메인; +var twoDimensions: domain(2) = {-2..2,0..2}; // 범위의 곱에 대한 2D 도메인 +var thirdDim: range = 1..16; +var threeDims: domain(3) = {thirdDim, 1..10, 5..10}; // 범위 변수 사용 + +// 도메인 크기도 조정할 수 있습니다. +var resizedDom = {1..10}; +writeln("before, resizedDom = ", resizedDom); +resizedDom = {-10..#10}; +writeln("after, resizedDom = ", resizedDom); + +// 인덱스는 튜플로 반복할 수 있습니다. +for idx in twoDimensions do + write(idx, ", "); +writeln(); + +// 이러한 튜플은 구조를 해제할 수도 있습니다. +for (x,y) in twoDimensions { + write("(", x, ", ", y, ")", ", "); +} +writeln(); + +// 연관 도메인은 집합처럼 작동합니다. +var stringSet: domain(string); // 빈 문자열 집합 +stringSet += "a"; +stringSet += "b"; +stringSet += "c"; +stringSet += "a"; // 중복 추가 "a" +stringSet -= "c"; // "c" 제거 +writeln(stringSet.sorted()); + +// 연관 도메인은 리터럴 구문을 가질 수도 있습니다. +var intSet = {1, 2, 4, 5, 100}; + +// 범위와 도메인은 모두 슬라이스하여 인덱스의 교집합이 있는 범위나 도메인을 생성할 수 있습니다. +var rangeA = 1.. ; // 1에서 무한대까지의 범위 +var rangeB = ..5; // 음의 무한대에서 5까지의 범위 +var rangeC = rangeA[rangeB]; // 결과 범위는 1..5입니다. +writeln((rangeA, rangeB, rangeC)); + +var domainA = {1..10, 5..20}; +var domainB = {-5..5, 1..10}; +var domainC = domainA[domainB]; +writeln((domainA, domainB, domainC)); + +/* +배열 +*/ + +// 배열은 다른 언어의 배열과 유사합니다. +// 크기는 인덱스를 나타내는 도메인을 사용하여 정의됩니다. +var intArray: [1..10] int; +var intArray2: [{1..10}] int; // 동일 + +// 대괄호나 괄호를 사용하여 액세스할 수 있습니다. +for i in 1..10 do + intArray[i] = -i; +writeln(intArray); + +// ``intArray[0]``에 액세스할 수 없습니다. 왜냐하면 정의한 인덱스 집합 ``{1..10}`` 외부에 존재하기 때문입니다. +// ``intArray[11]``도 같은 이유로 불법입니다. +var realDomain: domain(2) = {1..5,1..7}; +var realArray: [realDomain] real; +var realArray2: [1..5,1..7] real; // 동일 +var realArray3: [{1..5,1..7}] real; // 동일 + +for i in 1..5 { + for j in realDomain.dim(2) { // 도메인의 2차원만 사용 + realArray[i,j] = -1.61803 * i + 0.5 * j; // 인덱스 목록을 사용하여 액세스 + var idx: 2*int = (i,j); // 참고: 'index'는 키워드입니다. + realArray[idx] = - realArray[(i,j)]; // 튜플을 사용하여 인덱싱 + } +} + +// 배열에는 멤버로 도메인이 있으며 일반적인 방식으로 반복할 수 있습니다. +for idx in realArray.domain { // 다시 말하지만, idx는 2*int 튜플입니다. + realArray[idx] = 1 / realArray[idx[1], idx[2]]; // 튜플 및 목록으로 액세스 +} + +writeln(realArray); + +// 배열의 값은 직접 반복할 수도 있습니다. +var rSum: real = 0; +for value in realArray { + rSum += value; // 값 읽기 + value = rSum; // 값 쓰기 +} +writeln(rSum, "\n", realArray); + +// 연관 배열(사전)은 연관 도메인을 사용하여 만들 수 있습니다. +var dictDomain: domain(string) = { "one", "two", "three"}; +var dict: [dictDomain] int = ["one" => 1, "two" => 2, "three" => 3]; + +for key in dictDomain.sorted() do + writeln(dict[key]); + +// 배열은 몇 가지 다른 방식으로 서로 할당할 수 있습니다. +// 이 배열은 예제에서 사용됩니다. +var thisArray : [0..5] int = [0,1,2,3,4,5]; +var thatArray : [0..5] int; + +// 첫째, 하나를 다른 하나에 간단히 할당합니다. 이것은 ``thisArray``를 ``thatArray``에 복사하는 것이지 참조를 만드는 것이 아닙니다. 따라서 ``thisArray``를 수정해도 ``thatArray``는 수정되지 않습니다. + +thatArray = thisArray; +thatArray[1] = -1; +writeln((thisArray, thatArray)); + +// 한 배열의 슬라이스를 다른 배열의 슬라이스(동일한 크기)에 할당합니다. +thatArray[4..5] = thisArray[1..2]; +writeln((thisArray, thatArray)); + +// 연산은 배열에 대해 승격될 수도 있습니다. 'thisPlusThat'도 배열입니다. +var thisPlusThat = thisArray + thatArray; +writeln(thisPlusThat); + +// 계속해서, 배열과 루프는 표현식일 수도 있으며, 여기서 루프 본문의 표현식은 각 반복의 결과입니다. +var arrayFromLoop = for i in 1..10 do i; +Writeln(arrayFromLoop); + +// 표현식은 if-표현식으로 필터링할 때와 같이 아무것도 반환하지 않을 수 있습니다. +var evensOrFives = for i in 1..10 do if (i % 2 == 0 || i % 5 == 0) then i; + +Writeln(arrayFromLoop); + +// 배열 표현식은 대괄호 표기법으로도 작성할 수 있습니다. +// 참고: 이 구문은 나중에 논의될 ``forall`` 병렬 개념을 사용합니다. +var evensOrFivesAgain = [i in 1..10] if (i % 2 == 0 || i % 5 == 0) then i; + +// 배열의 값에 대해서도 작성할 수 있습니다. +arrayFromLoop = [value in arrayFromLoop] value + 1; + + +/* +프로시저 +*/ + +// Chapel 프로시저는 다른 언어의 함수와 유사한 구문을 가집니다. +proc fibonacci(n : int) : int { + if n <= 1 then return n; + return fibonacci(n-1) + fibonacci(n-2); +} + +// 입력 매개변수는 제네릭 프로시저를 만들기 위해 유형이 지정되지 않을 수 있습니다. +proc doublePrint(thing): void { + write(thing, " ", thing, "\n"); +} + +// 반환 유형은 컴파일러가 파악할 수 있는 한 추론될 수 있습니다. +proc addThree(n) { + return n + 3; +} + +doublePrint(addThree(fibonacci(20))); + +// 가변 개수의 매개변수를 사용하는 것도 가능합니다. +proc maxOf(x ...?k) { + // x는 k개의 요소를 가진 한 가지 유형의 튜플을 참조합니다. + var maximum = x[1]; + for i in 2..k do maximum = if maximum < x[i] then x[i] else maximum; + return maximum; +} +writeln(maxOf(1, -10, 189, -9071982, 5, 17, 20001, 42)); + +// 프로시저는 기본 매개변수 값을 가질 수 있으며, +// 매개변수는 순서에 상관없이 호출에서 이름으로 지정할 수 있습니다. +proc defaultsProc(x: int, y: real = 1.2634): (int,real) { + return (x,y); +} + +writeln(defaultsProc(10)); +writeln(defaultsProc(x=11)); +writeln(defaultsProc(x=12, y=5.432)); +writeln(defaultsProc(y=9.876, x=13)); + +// ``?`` 연산자는 쿼리 연산자라고 하며, 튜플이나 배열 크기 및 제네릭 유형과 같은 미정 값을 사용하는 데 사용됩니다. +// 예를 들어, 매개변수로 배열을 사용하는 경우입니다. 쿼리 연산자는 ``A``의 도메인을 결정하는 데 사용됩니다. 이것은 반환 유형을 정의하는 데 유용하지만 필수는 아닙니다. +proc invertArray(A: [?D] int): [D] int{ + for a in A do a = -a; + return A; +} + +writeln(invertArray(intArray)); + +// 제네릭 프로시저에 대한 인수의 유형을 쿼리할 수 있습니다. +// 여기서 우리는 동일한 유형의 두 인수를 사용하는 프로시저를 정의하지만 해당 유형이 무엇인지는 정의하지 않습니다. +proc genericProc(arg1 : ?valueType, arg2 : valueType): void { + select(valueType) { + when int do writeln(arg1, " and ", arg2, " are ints"); + when real do writeln(arg1, " and ", arg2, " are reals"); + otherwise writeln(arg1, " and ", arg2, " are somethings!"); + } +} + +genericProc(1, 2); +genericProc(1.2, 2.3); +genericProc(1.0+2.0i, 3.0+4.0i); + +// ``where`` 절을 사용하여 다형성의 한 형태를 적용할 수도 있습니다. +// 이렇게 하면 컴파일러가 사용할 함수를 결정할 수 있습니다. +// 참고: 즉, 모든 정보는 컴파일 타임에 알려져야 합니다. +// arg의 param 수정자는 이 제약 조건을 적용하는 데 사용됩니다. +proc whereProc(param N : int): void + where (N > 0) { + writeln("N is greater than 0"); +} + +proc whereProc(param N : int): void + where (N < 0) { + writeln("N is less than 0"); +} + +whereProc(10); +whereProc(-1); + +// ``whereProc(0)``은 ``where`` 절의 조건을 만족하는 함수가 없기 때문에 컴파일러 오류가 발생합니다. +// ``where`` 절이 없는 ``whereProc``를 정의하여 다른 모든 경우(이 경우 하나만 있음)를 포괄하는 캐치올로 사용할 수 있습니다. + +// ``where`` 절은 인수 유형에 따라 제약 조건을 지정하는 데에도 사용할 수 있습니다. +proc whereType(x: ?t) where t == int { + writeln("Inside 'int' version of 'whereType': ", x); +} + +proc whereType(x: ?t) { + writeln("Inside general version of 'whereType': ", x); +} + +whereType(42); +whereType("hello"); + +/* +의도 +*/ + +/* 인수에 대한 의도 수정자는 해당 인수가 프로시저에 전달되는 방식을 전달합니다. + + * in: 인수를 복사하지만 출력하지 않음 + * out: 인수를 출력하지만 복사하지 않음 + * inout: 인수를 복사하고 출력함 + * ref: 참조로 인수 전달 +*/ +proc intentsProc(in inarg, out outarg, inout inoutarg, ref refarg) { + writeln("Inside Before: ", (inarg, outarg, inoutarg, refarg)); + inarg = inarg + 100; + outarg = outarg + 100; + inoutarg = inoutarg + 100; + refarg = refarg + 100; + writeln("Inside After: ", (inarg, outarg, inoutarg, refarg)); +} + +var inVar: int = 1; +var outVar: int = 2; +var inoutVar: int = 3; +var refVar: int = 4; +writeln("Outside Before: ", (inVar, outVar, inoutVar, refVar)); +intentsProc(inVar, outVar, inoutVar, refVar); +Writeln("Outside After: ", (inVar, outVar, inoutVar, refVar)); + +// 마찬가지로 반환 유형에 대한 의도를 정의할 수 있습니다. +// ``refElement``는 배열의 요소에 대한 참조를 반환합니다. +// 이것은 데이터 구조의 요소에 대한 참조가 메서드나 반복기를 통해 반환되는 클래스 메서드에 더 실용적입니다. +proc refElement(array : [?D] ?T, idx) ref : T { + return array[idx]; +} + +var myChangingArray : [1..5] int = [1,2,3,4,5]; +writeln(myChangingArray); +ref refToElem = refElement(myChangingArray, 5); // ref 변수에 요소에 대한 참조 저장 +writeln(refToElem); +refToElem = -2; // 배열의 실제 값을 수정하는 참조 수정 +writeln(refToElem); +Writeln(myChangingArray); + +/* +연산자 정의 +*/ + +// Chapel은 연산자를 오버로드할 수 있습니다. +// 단항 연산자를 정의할 수 있습니다: +// ``+ - ! ~`` +// 이항 연산자: +// ``+ - * / % ** == <= >= < > << >> & | ˆ by`` +// ``+= -= *= /= %= **= &= |= ˆ= <<= >>= <=>` + +// 부울 배타적 논리합 연산자. +proc ^(left : bool, right : bool): bool { + return (left || right) && !(left && right); +} + +writeln(true ^ true); +writeln(false ^ true); +writeln(true ^ false); +writeln(false ^ false); + +// 해당 유형의 튜플을 반환하는 모든 두 유형에 대해 ``*`` 연산자를 정의합니다. +proc *(left : ?ltype, right : ?rtype): (ltype, rtype) { + writeln(" In our '*' overload!"); + return (left, right); +} + +writeln(1 * "a"); // ``*`` 연산자를 사용합니다. +writeln(1 * 2); // 기본 ``*`` 연산자를 사용합니다. + +// 참고: 오버로드에 부주의하면 모든 것을 망칠 수 있습니다. +// 이것은 모든 것을 망칠 것입니다. 하지 마십시오. + +/* + + proc +(left: int, right: int): int { + return left - right; + } +*/ + +/* +반복기 +*/ + +// 반복기는 프로시저의 자매이며, 프로시저에 대한 거의 모든 것이 반복기에도 적용됩니다. 그러나 단일 값을 반환하는 대신 반복기는 루프에 여러 값을 생성할 수 있습니다. +// +// 이것은 반복을 정의하는 코드를 루프 본문과 분리할 수 있으므로 복잡한 반복 집합이나 순서가 필요할 때 유용합니다. +iter oddsThenEvens(N: int): int { + for i in 1..N by 2 do + yield i; // 반환하는 대신 값을 생성합니다. + for i in 2..N by 2 do + yield i; +} + +for i in oddsThenEvens(10) do write(i, ", "); +Writeln(); + +// 반복기는 조건부로 생성할 수도 있으며, 그 결과는 아무것도 아닐 수 있습니다. +iter absolutelyNothing(N): int { + for i in 1..N { + if N < i { // 항상 거짓 + yield i; // Yield 문은 절대 발생하지 않습니다. + } + } +} + +for i in absolutelyNothing(10) { + writeln("Woa there! absolutelyNothing yielded ", i); +} + +// ``zip()``을 사용하여 두 개 이상의 반복기(반복 횟수가 동일한)를 함께 묶어 단일 압축 반복기를 만들 수 있습니다. 여기서 압축 반복기의 각 반복은 각 반복기에서 생성된 하나의 값의 튜플을 생성합니다. +for (positive, negative) in zip(1..5, -5..-1) do + writeln((positive, negative)); + +// 지퍼 반복은 배열, 배열 슬라이스 및 배열/루프 표현식의 할당에 매우 중요합니다. +var fromThatArray : [1..#5] int = [1,2,3,4,5]; +var toThisArray : [100..#5] int; + +// 일부 지퍼 작업은 다른 작업을 구현합니다. +// 첫 번째 문과 루프는 동일합니다. +toThisArray = fromThatArray; +for (i,j) in zip(toThisArray.domain, fromThatArray.domain) { + toThisArray[i] = fromThatArray[j]; +} + +// 이 두 덩어리도 동일합니다. +toThisArray = [j in -100..#5] j; +Writeln(toThisArray); + +for (i, j) in zip(toThisArray.domain, -100..#5) { + toThisArray[i] = j; +} +Writeln(toThisArray); + +// 이 문이 런타임 오류를 나타내는 이유를 이해하는 데 매우 중요합니다. + +/* + var iterArray : [1..10] int = [i in 1..10] if (i % 2 == 1) then i; +*/ + +// 배열의 도메인과 루프 표현식의 크기가 같더라도 표현식의 본문은 반복기로 생각할 수 있습니다. +// 반복기는 아무것도 생성하지 않을 수 있으므로 해당 반복기는 배열이나 루프의 도메인과 다른 수의 항목을 생성하며 이는 허용되지 않습니다. + +/* +클래스 +*/ +// 클래스는 C++ 및 Java의 클래스와 유사하며 힙에 할당됩니다. +class MyClass { + +// 멤버 변수 + var memberInt : int; + var memberBool : bool = true; + +// 기본적으로 이니셜라이저를 정의하지 않는 모든 클래스는 컴파일러에서 생성된 이니셜라이저를 가져옵니다. 이 이니셜라이저는 필드당 하나의 인수를 가지며 필드의 초기 값을 인수의 기본값으로 사용합니다. +// 또는 사용자는 다음 주석 처리된 루틴에 표시된 대로 이니셜라이저를 수동으로 정의할 수 있습니다. +// +/* // proc init(val : real) { + // this.memberInt = ceil(val): int; + // } +*/ + +// 명시적으로 정의된 디이니셜라이저. +// 우리가 작성하지 않았다면 컴파일러에서 생성된 디이니셜라이저를 얻었을 것입니다. 이 디이니셜라이저는 빈 본문을 가집니다. + proc deinit() { + writeln("MyClass deinitializer called ", (this.memberInt, this.memberBool)); + } + +// 클래스 메서드. + proc setMemberInt(val: int) { + this.memberInt = val; + } + + proc setMemberBool(val: bool) { + this.memberBool = val; + } + + proc getMemberInt(): int{ + return this.memberInt; + } + + proc getMemberBool(): bool { + return this.memberBool; + } +} // MyClass 끝 + +// 컴파일러에서 생성된 이니셜라이저를 호출하고 memberBool에 기본값을 사용합니다. +{ + var myObject = new owned MyClass(10); + myObject = new owned MyClass(memberInt = 10); // 동일 + writeln(myObject.getMemberInt()); + + // 동일하지만 memberBool 값을 명시적으로 제공합니다. + var myDiffObject = new owned MyClass(-1, true); + myDiffObject = new owned MyClass(memberInt = -1, + memberBool = true); // 동일 + writeln(myDiffObject); + + // 유사하지만 memberInt의 기본값에 의존하고 memberBool을 전달합니다. + var myThirdObject = new owned MyClass(memberBool = true); + writeln(myThirdObject); + + // 위의 사용자 정의 이니셜라이저가 주석 처리되지 않았다면 다음 호출을 할 수 있습니다: + // + /* // var myOtherObject = new MyClass(1.95); + // myOtherObject = new MyClass(val = 1.95); + // writeln(myOtherObject.getMemberInt()); + */ + + // 클래스에 대한 연산자를 정의할 수도 있지만 + // 정의는 클래스 정의 외부에 있어야 합니다. + proc +(A : MyClass, B : MyClass) : owned MyClass { + return + new owned MyClass(memberInt = A.getMemberInt() + B.getMemberInt(), + memberBool = A.getMemberBool() || B.getMemberBool()); + } + + var plusObject = myObject + myDiffObject; + writeln(plusObject); + + // 객체 소멸: deinit() 루틴을 호출하고 메모리를 해제합니다. + // ``unmanaged`` 변수는 ``delete``를 호출해야 합니다. + // ``owned`` 변수는 범위를 벗어날 때 소멸됩니다. +} + +// 클래스는 하나 이상의 부모 클래스에서 상속할 수 있습니다. +class MyChildClass : MyClass { + var memberComplex: complex; +} + +// 제네릭 클래스의 예입니다. +class GenericClass { + type classType; + var classDomain: domain(1); + var classArray: [classDomain] classType; + +// 명시적 초기화 프로그램. + proc init(type classType, elements : int) { + this.classType = classType; + this.classDomain = {1..elements}; + // 모든 제네릭 및 const 필드는 슈퍼클래스 초기화 프로그램 호출 전에 "1단계"에서 초기화해야 합니다. + } + +// 복사 스타일 초기화 프로그램. +// 참고: 첫 번째 인수의 유형인 기본값을 가진 유형 인수를 포함합니다. +// 이렇게 하면 초기화 프로그램이 다른 유형의 클래스를 복사하고 즉시 캐스팅할 수 있습니다. + proc init(other : GenericClass(?), + type classType = other.classType) { + this.classType = classType; + this.classDomain = other.classDomain; + this.classArray = for o in other do o: classType; // 복사 및 캐스팅 + } + +// GenericClass에 대괄호 표기법을 정의합니다. +// 객체가 일반 배열처럼 동작할 수 있도록 합니다. +// 즉, ``objVar[i]`` 또는 ``objVar(i)`` + proc this(i : int) ref : classType { + return this.classArray[i]; + } + +// 클래스에 대한 암시적 반복기를 정의합니다. +// 배열에서 루프에 값을 생성합니다. +// 즉, ``for i in objVar do ...`` + iter these() ref : classType { + for i in this.classDomain do + yield this[i]; + } +} // GenericClass 끝 + +// 클래스의 소유 인스턴스 할당 +var realList = new owned GenericClass(real, 10); + +// 정의한 대괄호 표기법을 사용하여 객체의 멤버 배열에 할당할 수 있습니다. +for i in realList.classDomain do realList[i] = i + 1.0; + +// 정의한 반복기를 사용하여 목록의 값을 반복할 수 있습니다. +for value in realList do write(value, ", "); +Writeln(); + +// 복사 초기화 프로그램을 사용하여 realList의 복사본을 만듭니다. +var copyList = new owned GenericClass(realList); +for value in copyList do write(value, ", "); +Writeln(); + +// realList의 복사본을 만들고 복사 초기화 프로그램을 사용하여 유형을 변경합니다. +var copyNewTypeList = new owned GenericClass(realList, int); +for value in copyNewTypeList do write(value, ", "); +Writeln(); + + +/* +모듈 +*/ + +// 모듈은 Chapel이 네임스페이스를 관리하는 방법입니다. +// 이러한 모듈을 포함하는 파일은 모듈 이름 뒤에 이름을 지정할 필요가 없습니다(Java에서와 같이). 그러나 파일은 암시적으로 모듈 이름을 지정합니다. +// 예를 들어, 이 파일은 암시적으로 ``learnChapelInYMinutes`` 모듈의 이름을 지정합니다. + +module OurModule { + +// 다른 모듈 내에서 모듈을 사용할 수 있습니다. +// Time은 표준 모듈 중 하나입니다. + use Time; + +// 병렬 처리 섹션에서 이 프로시저를 사용합니다. + proc countdown(seconds: int) { + for i in 1..seconds by -1 { + writeln(i); + sleep(1); + } + } + +// 임의로 깊은 모듈 중첩을 만들 수 있습니다. +// 즉, OurModule의 하위 모듈 + module ChildModule { + proc foo() { + writeln("ChildModule.foo()") + } + } + + module SiblingModule { + proc foo() { + writeln("SiblingModule.foo()") + } + } +} // OurModule 끝 + +// ``OurModule``을 사용하면 사용하는 모든 모듈도 사용됩니다. +// ``OurModule``이 ``Time``을 사용하므로 우리도 ``Time``을 사용합니다. +use OurModule; + +// 이 시점에서 ``ChildModule`` 또는 ``SiblingModule``을 사용하지 않았으므로 해당 기호(즉, ``foo``)를 사용할 수 없습니다. 그러나 모듈 이름은 사용할 수 있으며 이를 통해 ``foo()``를 명시적으로 호출할 수 있습니다. +SiblingModule.foo(); +OurModule.ChildModule.foo(); + +// 이제 ``ChildModule``을 사용하여 정규화되지 않은 호출을 활성화합니다. +use ChildModule; +foo(); + +/* +병렬성 +*/ + +// 다른 언어에서는 병렬 처리가 일반적으로 복잡한 라이브러리와 이상한 클래스 구조 계층으로 수행됩니다. +// Chapel은 언어에 내장되어 있습니다. + +// 주 프로시저를 선언할 수 있지만 주 프로시저 위의 모든 코드는 여전히 실행됩니다. +proc main() { + +// ``begin`` 문은 해당 문의 본문을 하나의 새 작업으로 분리합니다. +// ``sync`` 문은 주 작업의 진행이 자식이 다시 동기화될 때까지 진행되지 않도록 합니다. + + sync { + begin { // 새 작업 본문의 시작 + var a = 0; + for i in 1..1000 do a += 1; + writeln("Done: ", a); + } // 새 작업 본문의 끝 + writeln("spun off a task!"); + } + writeln("Back together"); + + proc printFibb(n: int) { + writeln("fibonacci(",n,") = ", fibonacci(n)); + } + +// ``cobegin`` 문은 본문의 각 문을 하나의 새 작업으로 분리합니다. 여기서 각 문의 인쇄가 어떤 순서로든 발생할 수 있음을 주목하십시오. + cobegin { + printFibb(20); // 새 작업 + printFibb(10); // 새 작업 + printFibb(5); // 새 작업 + { + // 이것은 중첩된 문 본문이므로 부모 문에 대한 단일 문이며 단일 작업으로 실행됩니다. + writeln("this gets"); + writeln("executed as"); + writeln("a whole"); + } + } + +// ``coforall`` 루프는 각 반복에 대해 새 작업을 생성합니다. +// 다시 말하지만, 인쇄가 어떤 순서로든 발생한다는 것을 알 수 있습니다. +// 참고: ``coforall``은 작업을 생성하는 데만 사용해야 합니다! +// 구조를 반복하는 데 사용하는 것은 매우 나쁜 생각입니다! + var num_tasks = 10; // 원하는 작업 수 + coforall taskID in 1..num_tasks { + writeln("Hello from task# ", taskID); + } + +// ``forall`` 루프는 또 다른 병렬 루프이지만, 더 적은 수의 작업, 특히 ``--dataParTasksPerLocale=`` 수의 작업만 생성합니다. + forall i in 1..100 { + write(i, ", "); + } + writeln(); + +// 여기서 순서대로 된 섹션이 있고 그 뒤에 따르지 않는 섹션이 있음을 알 수 있습니다(예: 1, 2, 3, 7, 8, 9, 4, 5, 6,). +// 이것은 각 작업이 1..10 범위의 청크(1..3, 4..6 또는 7..9)를 직렬로 수행하지만 각 작업은 병렬로 발생하기 때문입니다. 결과는 컴퓨터 및 구성에 따라 다를 수 있습니다. + +// ``forall`` 및 ``coforall`` 루프 모두에 대해 부모 작업의 실행은 모든 자식이 동기화될 때까지 계속되지 않습니다. + +// ``forall`` 루프는 배열에 대한 병렬 반복에 특히 유용합니다. +// (가지고 있는 코어 수에 따라) 병렬 루프가 직렬 루프보다 더 빨리 진행되었음을 알 수 있습니다. +// 이 문이 런타임 오류를 나타내는 이유를 이해하는 데 매우 중요합니다. + +/* + var iterArray : [1..10] int = [i in 1..10] if (i % 2 == 1) then i; +*/ + +// 배열의 도메인과 루프 표현식의 크기가 같더라도 표현식의 본문은 반복기로 생각할 수 있습니다. +// 반복기는 아무것도 생성하지 않을 수 있으므로 해당 반복기는 배열이나 루프의 도메인과 다른 수의 항목을 생성하며 이는 허용되지 않습니다. + +/* +클래스 +*/ +// 클래스는 C++ 및 Java의 클래스와 유사하며 힙에 할당됩니다. +class MyClass { + +// 멤버 변수 + var memberInt : int; + var memberBool : bool = true; + +// 기본적으로 이니셜라이저를 정의하지 않는 모든 클래스는 컴파일러에서 생성된 이니셜라이저를 가져옵니다. 이 이니셜라이저는 필드당 하나의 인수를 가지며 필드의 초기 값을 인수의 기본값으로 사용합니다. +// 또는 사용자는 다음 주석 처리된 루틴에 표시된 대로 이니셜라이저를 수동으로 정의할 수 있습니다. +// +/* // proc init(val : real) { + // this.memberInt = ceil(val): int; + // } +*/ + +// 명시적으로 정의된 디이니셜라이저. +// 우리가 작성하지 않았다면 컴파일러에서 생성된 디이니셜라이저를 얻었을 것입니다. 이 디이니셜라이저는 빈 본문을 가집니다. + proc deinit() { + writeln("MyClass deinitializer called ", (this.memberInt, this.memberBool)); + } + +// 클래스 메서드. + proc setMemberInt(val: int) { + this.memberInt = val; + } + + proc setMemberBool(val: bool) { + this.memberBool = val; + } + + proc getMemberInt(): int{ + return this.memberInt; + } + + proc getMemberBool(): bool { + return this.memberBool; + } +} // MyClass 끝 + +// 컴파일러에서 생성된 이니셜라이저를 호출하고 memberBool에 기본값을 사용합니다. +{ + var myObject = new owned MyClass(10); + myObject = new owned MyClass(memberInt = 10); // 동일 + writeln(myObject.getMemberInt()); + + // 동일하지만 memberBool 값을 명시적으로 제공합니다. + var myDiffObject = new owned MyClass(-1, true); + myDiffObject = new owned MyClass(memberInt = -1, + memberBool = true); // 동일 + writeln(myDiffObject); + + // 유사하지만 memberInt의 기본값에 의존하고 memberBool을 전달합니다. + var myThirdObject = new owned MyClass(memberBool = true); + writeln(myThirdObject); + + // 위의 사용자 정의 이니셜라이저가 주석 처리되지 않았다면 다음 호출을 할 수 있습니다: + // + /* // var myOtherObject = new MyClass(1.95); + // myOtherObject = new MyClass(val = 1.95); + // writeln(myOtherObject.getMemberInt()); + */ + + // 클래스에 대한 연산자를 정의할 수도 있지만 + // 정의는 클래스 정의 외부에 있어야 합니다. + proc +(A : MyClass, B : MyClass) : owned MyClass { + return + new owned MyClass(memberInt = A.getMemberInt() + B.getMemberInt(), + memberBool = A.getMemberBool() || B.getMemberBool()); + } + + var plusObject = myObject + myDiffObject; + writeln(plusObject); + + // 객체 소멸: deinit() 루틴을 호출하고 메모리를 해제합니다. + // ``unmanaged`` 변수는 ``delete``를 호출해야 합니다. + // ``owned`` 변수는 범위를 벗어날 때 소멸됩니다. +} + +// 클래스는 하나 이상의 부모 클래스에서 상속할 수 있습니다. +class MyChildClass : MyClass { + var memberComplex: complex; +} + +// 제네릭 클래스의 예입니다. +class GenericClass { + type classType; + var classDomain: domain(1); + var classArray: [classDomain] classType; + +// 명시적 초기화 프로그램. + proc init(type classType, elements : int) { + this.classType = classType; + this.classDomain = {1..elements}; + // 모든 제네릭 및 const 필드는 슈퍼클래스 초기화 프로그램 호출 전에 "1단계"에서 초기화해야 합니다. + } + +// 복사 스타일 초기화 프로그램. +// 참고: 첫 번째 인수의 유형인 기본값을 가진 유형 인수를 포함합니다. +// 이렇게 하면 초기화 프로그램이 다른 유형의 클래스를 복사하고 즉시 캐스팅할 수 있습니다. + proc init(other : GenericClass(?), + type classType = other.classType) { + this.classType = classType; + this.classDomain = other.classDomain; + this.classArray = for o in other do o: classType; // 복사 및 캐스팅 + } + +// GenericClass에 대괄호 표기법을 정의합니다. +// 객체가 일반 배열처럼 동작할 수 있도록 합니다. +// 즉, ``objVar[i]`` 또는 ``objVar(i)`` + proc this(i : int) ref : classType { + return this.classArray[i]; + } + +// 클래스에 대한 암시적 반복기를 정의합니다. +// 배열에서 루프에 값을 생성합니다. +// 즉, ``for i in objVar do ...`` + iter these() ref : classType { + for i in this.classDomain do + yield this[i]; + } +} // GenericClass 끝 + +// 클래스의 소유 인스턴스 할당 +var realList = new owned GenericClass(real, 10); + +// 정의한 대괄호 표기법을 사용하여 객체의 멤버 배열에 할당할 수 있습니다. +for i in realList.classDomain do realList[i] = i + 1.0; + +// 정의한 반복기를 사용하여 목록의 값을 반복할 수 있습니다. +for value in realList do write(value, ", "); +Writeln(); + +// 복사 초기화 프로그램을 사용하여 realList의 복사본을 만듭니다. +var copyList = new owned GenericClass(realList); +for value in copyList do write(value, ", "); +Writeln(); + +// realList의 복사본을 만들고 복사 초기화 프로그램을 사용하여 유형을 변경합니다. +var copyNewTypeList = new owned GenericClass(realList, int); +for value in copyNewTypeList do write(value, ", "); +Writeln(); + + +/* +모듈 +*/ + +// 모듈은 Chapel이 네임스페이스를 관리하는 방법입니다. +// 이러한 모듈을 포함하는 파일은 모듈 이름 뒤에 이름을 지정할 필요가 없습니다(Java에서와 같이). 그러나 파일은 암시적으로 모듈 이름을 지정합니다. +// 예를 들어, 이 파일은 암시적으로 ``learnChapelInYMinutes`` 모듈의 이름을 지정합니다. + +module OurModule { + +// 다른 모듈 내에서 모듈을 사용할 수 있습니다. +// Time은 표준 모듈 중 하나입니다. + use Time; + +// 병렬 처리 섹션에서 이 프로시저를 사용합니다. + proc countdown(seconds: int) { + for i in 1..seconds by -1 { + writeln(i); + sleep(1); + } + } + +// 임의로 깊은 모듈 중첩을 만들 수 있습니다. +// 즉, OurModule의 하위 모듈 + module ChildModule { + proc foo() { + writeln("ChildModule.foo()") + } + } + + module SiblingModule { + proc foo() { + writeln("SiblingModule.foo()") + } + } +} // OurModule 끝 + +// ``OurModule``을 사용하면 사용하는 모든 모듈도 사용됩니다. +// ``OurModule``이 ``Time``을 사용하므로 우리도 ``Time``을 사용합니다. +use OurModule; + +// 이 시점에서 ``ChildModule`` 또는 ``SiblingModule``을 사용하지 않았으므로 해당 기호(즉, ``foo``)를 사용할 수 없습니다. 그러나 모듈 이름은 사용할 수 있으며 이를 통해 ``foo()``를 명시적으로 호출할 수 있습니다. +SiblingModule.foo(); +OurModule.ChildModule.foo(); + +// 이제 ``ChildModule``을 사용하여 정규화되지 않은 호출을 활성화합니다. +use ChildModule; +foo(); + +/* +병렬성 +*/ + +// 다른 언어에서는 병렬 처리가 일반적으로 복잡한 라이브러리와 이상한 클래스 구조 계층으로 수행됩니다. +// Chapel은 언어에 내장되어 있습니다. + +// 주 프로시저를 선언할 수 있지만 주 프로시저 위의 모든 코드는 여전히 실행됩니다. +proc main() { + +// ``begin`` 문은 해당 문의 본문을 하나의 새 작업으로 분리합니다. +// ``sync`` 문은 주 작업의 진행이 자식이 다시 동기화될 때까지 진행되지 않도록 합니다. + + sync { + begin { // 새 작업 본문의 시작 + var a = 0; + for i in 1..1000 do a += 1; + writeln("Done: ", a); + } // 새 작업 본문의 끝 + writeln("spun off a task!"); + } + writeln("Back together"); + + proc printFibb(n: int) { + writeln("fibonacci(",n,") = ", fibonacci(n)); + } + +// ``cobegin`` 문은 본문의 각 문을 하나의 새 작업으로 분리합니다. 여기서 각 문의 인쇄가 어떤 순서로든 발생할 수 있음을 주목하십시오. + cobegin { + printFibb(20); // 새 작업 + printFibb(10); // 새 작업 + printFibb(5); // 새 작업 + { + // 이것은 중첩된 문 본문이므로 부모 문에 대한 단일 문이며 단일 작업으로 실행됩니다. + writeln("this gets"); + writeln("executed as"); + writeln("a whole"); + } + } + +// ``coforall`` 루프는 각 반복에 대해 새 작업을 생성합니다. +// 다시 말하지만, 인쇄가 어떤 순서로든 발생한다는 것을 알 수 있습니다. +// 참고: ``coforall``은 작업을 생성하는 데만 사용해야 합니다! +// 구조를 반복하는 데 사용하는 것은 매우 나쁜 생각입니다! + var num_tasks = 10; // 원하는 작업 수 + coforall taskID in 1..num_tasks { + writeln("Hello from task# ", taskID); + } + +// ``forall`` 루프는 또 다른 병렬 루프이지만, 더 적은 수의 작업, 특히 ``--dataParTasksPerLocale=`` 수의 작업만 생성합니다. + forall i in 1..100 { + write(i, ", "); + } + writeln(); + +// 여기서 순서대로 된 섹션이 있고 그 뒤에 따르지 않는 섹션이 있음을 알 수 있습니다(예: 1, 2, 3, 7, 8, 9, 4, 5, 6,). +// 이것은 각 작업이 1..10 범위의 청크(1..3, 4..6 또는 7..9)를 직렬로 수행하지만 각 작업은 병렬로 발생하기 때문입니다. 결과는 컴퓨터 및 구성에 따라 다를 수 있습니다. + +// ``forall`` 및 ``coforall`` 루프 모두에 대해 부모 작업의 실행은 모든 자식이 동기화될 때까지 계속되지 않습니다. + +// ``forall`` 루프는 배열에 대한 병렬 반복에 특히 유용합니다. +// 병렬 루프가 직렬 루프보다 얼마나 빠른지 확인하기 위해 실험을 실행해 보겠습니다. + use Time; // Timer 객체를 사용하려면 Time 모듈을 가져옵니다. + var timer: Timer; + var myBigArray: [{1..4000,1..4000}] real; // 쓸 큰 배열 + +// 직렬 실험: + timer.start(); // 타이머 시작 + for (x,y) in myBigArray.domain { // 직렬 반복 + myBigArray[x,y] = (x:real) / (y:real); + } + timer.stop(); // 타이머 중지 + writeln("Serial: ", timer.elapsed()); // 경과 시간 인쇄 + timer.clear(); // 병렬 루프를 위해 타이머 지우기 + +// 병렬 실험: + timer.start(); // 타이머 시작 + forall (x,y) in myBigArray.domain { // 병렬 반복 + myBigArray[x,y] = (x:real) / (y:real); + } + timer.stop(); // 타이머 중지 + writeln("Parallel: ", timer.elapsed()); // 경과 시간 인쇄 + timer.clear(); + +// (가지고 있는 코어 수에 따라) 병렬 루프가 직렬 루프보다 더 빨리 진행되었음을 알 수 있습니다. + +// 이전에 설명한 대괄호 스타일 루프 표현식은 암시적으로 ``forall`` 루프를 사용합니다. + [val in myBigArray] val = 1 / val; // 병렬 연산 + +// 많은 언어에서 일반적인 원자 변수는 작업이 중단되지 않는 변수입니다. 따라서 여러 스레드가 원자 변수를 수정할 수 있으며 해당 값이 안전하다는 것을 알 수 있습니다. +// Chapel 원자 변수는 ``bool``, ``int``, ``uint`` 및 ``real`` 유형일 수 있습니다. + var uranium: atomic int; + uranium.write(238); // 변수를 원자적으로 씁니다. + writeln(uranium.read()); // 변수를 원자적으로 읽습니다. + +// 원자 연산은 함수로 설명되므로 자신만의 함수를 정의할 수 있습니다. + uranium.sub(3); // 변수를 원자적으로 뺍니다. + writeln(uranium.read()); + + var replaceWith = 239; + var was = uranium.exchange(replaceWith); + writeln("uranium was ", was, " but is now ", replaceWith); + + var isEqualTo = 235; + if uranium.compareAndSwap(isEqualTo, replaceWith) { + writeln("uranium was equal to ", isEqualTo, + " so replaced value with ", replaceWith); + } else { + writeln("uranium was not equal to ", isEqualTo, + " so value stays the same... whatever it was"); + } + + sync { + begin { // 리더 작업 + writeln("Reader: waiting for uranium to be ", isEqualTo); + uranium.waitFor(isEqualTo); + writeln("Reader: uranium was set (by someone) to ", isEqualTo); + } + + begin { // 라이터 작업 + writeln("Writer: will set uranium to the value ", isEqualTo, " in..."); + countdown(3); + uranium.write(isEqualTo); + } + } + +// ``sync`` 변수에는 두 가지 상태가 있습니다: 비어 있음과 가득 참. +// 비어 있는 변수를 읽거나 가득 찬 변수를 쓰면 변수가 다시 가득 차거나 비워질 때까지 기다립니다. + var someSyncVar$: sync int; // varName$는 법이 아닌 관례입니다. + sync { + begin { // 리더 작업 + writeln("Reader: waiting to read."); + var read_sync = someSyncVar$; + writeln("Reader: value is ", read_sync); + } + + begin { // 라이터 작업 + writeln("Writer: will write in..."); + countdown(3); + someSyncVar$ = 123; + } + } + +// ``single`` 변수는 한 번만 쓸 수 있습니다. 쓰지 않은 ``single``을 읽으면 대기하지만 변수에 값이 있으면 무기한 읽을 수 있습니다. + var someSingleVar$: single int; // varName$는 법이 아닌 관례입니다. + sync { + begin { // 리더 작업 + writeln("Reader: waiting to read."); + for i in 1..5 { + var read_single = someSingleVar$; + writeln("Reader: iteration ", i,", and the value is ", read_single); + } + } + + begin { // 라이터 작업 + writeln("Writer: will write in..."); + countdown(3); + someSingleVar$ = 5; // 처음이자 마지막 쓰기. + } + } + +// 다음은 원자 및 ``sync`` 변수를 사용하여 카운트다운 뮤텍스(멀티플렉서라고도 함)를 만드는 예입니다. + var count: atomic int; // 카운터 + var lock$: sync bool; // 뮤텍스 잠금 + + count.write(2); // 한 번에 두 개의 작업만 허용합니다. + lock$.writeXF(true); // lock$을 가득 참(잠금 해제)으로 설정합니다. + // 참고: 값은 실제로 중요하지 않고 상태만 중요합니다. + // (가득 참:잠금 해제 / 비어 있음:잠김) + // 또한 writeXF()는 상태(X)에 관계없이 동기화 변수를 채웁니다(F). + + coforall task in 1..5 { // 작업 생성 + // 장벽 생성 + do { + lock$; // lock$ 읽기(대기) + } while (count.read() < 1); // 자리가 생길 때까지 계속 대기 + + count.sub(1); // 카운터 감소 + lock$.writeXF(true); // lock$을 가득 참(신호)으로 설정 + + // 실제 '작업' + writeln("Task #", task, " doing work."); + sleep(2); + + count.add(1); // 카운터 증가 + lock$.writeXF(true); // lock$을 가득 참(신호)으로 설정 + } + +// 스캔 및 축소를 사용하여 전체 배열에 대해 ``+ * & | ^ && || min max minloc maxloc`` 연산을 정의할 수 있습니다. +// 축소는 전체 배열에 대해 연산을 적용하고 스칼라 값을 반환합니다. + var listOfValues: [1..10] int = [15,57,354,36,45,15,456,8,678,2]; + var sumOfValues = + reduce listOfValues; + var maxValue = max reduce listOfValues; // 'max'는 최대값만 제공합니다. + +// ``maxloc``은 최대값과 최대값의 인덱스를 제공합니다. +// 참고: zip 반복기를 사용하여 배열과 도메인을 함께 압축해야 합니다. + var (theMaxValue, idxOfMax) = maxloc reduce zip(listOfValues, + listOfValues.domain); + + writeln((sumOfValues, maxValue, idxOfMax, listOfValues[idxOfMax])); + +// 스캔은 연산을 점진적으로 적용하고 해당 인덱스에서 연산의 값이 있는 배열을 반환합니다. 이 연산은 배열을 ``array.domain.low``에서 ``array.domain.high``로 진행하면서 수행됩니다. + var runningSumOfValues = + scan listOfValues; + var maxScan = max scan listOfValues; + writeln(runningSumOfValues); + writeln(maxScan); +} // main() 끝 + +## 이 튜토리얼은 누구를 위한 것입니까? + +이 튜토리얼은 밧줄이 어떤 섬유 혼합물로 만들어졌는지, 어떻게 땋았는지, 또는 땋기 구성이 서로 어떻게 다른지에 대해 듣지 않고 채플의 요령을 배우고 싶은 사람들을 위한 것입니다. 놀랍도록 성능이 좋은 코드를 개발하는 방법을 가르쳐주지는 않으며, 완전하지도 않습니다. 자세한 내용은 [언어 사양](https://chapel-lang.org/docs/latest/language/spec.html) 및 [모듈 문서](https://chapel-lang.org/docs/latest/)를 참조하십시오. + +때때로 여기와 [Chapel 사이트](https://chapel-lang.org)를 다시 확인하여 더 많은 주제가 추가되었는지 또는 더 많은 튜토리얼이 만들어졌는지 확인하십시오. + +### 이 튜토리얼에 부족한 점: + +* [표준 모듈](https://chapel-lang.org/docs/latest/modules/standard.html) 설명 +* 다중 로케일(분산 메모리 시스템) +* 레코드 +* 병렬 반복기 + +## 귀하의 의견, 질문 및 발견은 개발자에게 중요합니다! + +Chapel 언어는 아직 활발하게 개발 중이므로 성능 및 언어 기능에 가끔 문제가 발생합니다. Chapel 개발팀에 발생하는 문제나 보고 싶은 기능에 대한 정보를 많이 제공할수록 언어가 더 좋아집니다. +개발자와 상호 작용하는 방법에는 여러 가지가 있습니다: + +* [Gitter 채팅](https://gitter.im/chapel-lang/chapel) +* [sourceforge 이메일 목록](https://sourceforge.net/p/chapel/mailman) + +컴파일러 개발에 정말 관심이 있거나 프로젝트에 기여하고 싶다면 [마스터 GitHub 리포지토리](https://github.com/chapel-lang/chapel)를 확인하십시오. +[Apache 2.0 라이선스](http://www.apache.org/licenses/LICENSE-2.0)에 따라 제공됩니다. + +## 컴파일러 설치 + +[공식 Chapel 문서에는 Chapel 컴파일러를 다운로드하고 컴파일하는 방법이 자세히 설명되어 있습니다.](https://chapel-lang.org/docs/usingchapel/QUICKSTART.html) + +Chapel은 일반적인 'nix 머신(및 cygwin)에 빌드하고 설치할 수 있습니다. +[최신 릴리스 버전 다운로드](https://github.com/chapel-lang/chapel/releases/)하고 다음과 같이 간단합니다. + +1. `tar -xvf chapel-.tar.gz` +2. `cd chapel-` +3. `source util/setchplenv.bash # 또는 .sh 또는 .csh 또는 .fish` +4. `make` +5. `make check # 선택 사항` + +터미널이 시작될 때마다 Chapel 디렉토리(`$CHPL_HOME`) 내에서 `source util/setchplenv.EXT`를 실행해야 하므로 시작 시 실행될 스크립트(.bashrc 등)에 해당 명령을 넣는 것이 좋습니다. + +Chapel은 Homebrew를 사용하여 macOS에 쉽게 설치할 수 있습니다. + +1. `brew update` +2. `brew install chapel` + +## 코드 컴파일 + +다른 컴파일러와 같이 빌드합니다: + +`chpl myFile.chpl -o myExe` + +주목할 만한 인수: + +* `--fast`: 여러 최적화를 활성화하고 배열 경계 검사를 비활성화합니다. 애플리케이션이 안정적일 때만 활성화해야 합니다. +* `--set =`: 컴파일 타임에 config param ``을 ``로 설정합니다. +* `--main-module `: 모듈 ``에 있는 main() 프로시저를 실행 파일의 main으로 사용합니다. +* `--module-dir `: 모듈 검색 경로에 ``를 포함합니다. + +``` \ No newline at end of file diff --git a/ko/chicken.md b/ko/chicken.md new file mode 100644 index 0000000000..ab95e57a61 --- /dev/null +++ b/ko/chicken.md @@ -0,0 +1,521 @@ +--- +name: "CHICKEN" +filename: CHICKEN.scm +contributors: + - ["Diwakar Wagle", "https://github.com/deewakar"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +CHICKEN은 Scheme 프로그래밍 언어의 구현으로, Scheme 프로그램을 C 코드로 컴파일하고 해석할 수 있습니다. CHICKEN은 R5RS 및 R7RS(작업 중) 표준과 많은 확장을 지원합니다. + + +```scheme +;; #!/usr/bin/env csi -s + +;; 다음과 같이 명령줄에서 CHICKEN REPL을 실행합니다: +;; $ csi + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 0. 구문 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 한 줄 주석은 세미콜론으로 시작합니다. + +#| 블록 주석 + 여러 줄에 걸쳐 작성할 수 있으며... + #| 중첩될 수 있습니다. + |# +|# + +;; S-표현식 주석은 표현식을 주석 처리하는 데 사용됩니다. +#; (display "nothing") ; 이 표현식을 버립니다. + +;; CHICKEN에는 두 가지 기본 구문이 있습니다: 원자(Atom)와 S-표현식(S-expression) +;; 원자는 그 자체로 평가되는 것입니다. +;; 숫자, 문자, 부울, 문자열 등 모든 내장 데이터 유형은 원자입니다. +;; 또한 원자는 기호, 식별자, 키워드, 프로시저 +;; 또는 빈 목록(null이라고도 함)일 수 있습니다. +'athing ;; => athing +'+' ;; => + ++ +;; => + +;; S-표현식(기호 표현식의 약자)은 하나 이상의 원자로 구성됩니다. +(quote +) ;; => + ; '+'를 쓰는 또 다른 방법 +(+ 1 2 3) ;; => 6 ; 이 S-표현식은 함수 호출로 평가됩니다. +'(+ 1 2 3) ;; => (+ 1 2 3) ; 목록으로 평가됩니다. + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 1. 기본 데이터 유형 및 연산자 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 숫자 +99999999999999999999 ;; 정수 +#b1010 ;; 이진수 ; => 10 +#o10 ;; 8진수 ; => 8 +#x8ded ;; 16진수 ; => 36333 +3.14 ;; 실수 +6.02e+23 +3/4 ;; 유리수 + +;;문자 및 문자열 +#\A ;; 문자 A +"Hello, World!" ;; 문자열은 고정 길이의 문자 배열입니다. + +;; 부울 +#t ;; 참 +#f ;; 거짓 + +;; 함수 호출은 (f x y z ...)로 작성됩니다. +;; 여기서 f는 함수이고 x,y,z, ...는 인수입니다. +(print "Hello, World!") ;; => Hello, World! +;; 형식화된 출력 +(printf "Hello, ~a.\n" "World") ;; => Hello, World. + +;; 명령줄 인수 인쇄 +(map print (command-line-arguments)) + +(list 'foo 'bar 'baz) ;; => (foo bar baz) +(string-append "pine" "apple") ;; => "pineapple" +(string-ref "tapioca" 3) ;; => #\i;; 문자 'i'는 인덱스 3에 있습니다. +(string->list "CHICKEN") ;; => (#\C #\H #\I #\C #\K #\E #\N) +(string-intersperse '("1" "2") ":") ;; => "1:2" +(string-split "1:2:3" ":") ;; => ("1" "2" "3") + + +;; 술어는 부울 값을 반환하는 특수 함수입니다. +(atom? #t) ;; => #t + +(symbol? #t) ;; => #f + +(symbol? '+') ;; => #t + +(procedure? +) ;; => #t + +(pair? '(1 2)) ;; => #t + +(pair? '(1 2 . 3)) ;; => #t + +(pair? '()) ;; => #f + +(list? '()) ;; => #t + + +;; 일부 산술 연산 + +(+ 1 1) ;; => 2 +(- 8 1) ;; => 7 +(* 10 2) ;; => 20 +(expt 2 3) ;; => 8 +(remainder 5 2) ;; => 1 +(/ 35 5) ;; => 7 +(/ 1 3) ;; => 0.333333333333333 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 2. 변수 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; define으로 변수를 만들 수 있습니다. +;; 변수 이름은 ()[]{}\",'`;#\를 제외한 모든 문자를 사용할 수 있습니다. +(define myvar 5) +myvar ;; => 5 + +;; 프로시저에 대한 별칭 +(define ** expt) +(** 2 3) ;; => 8 + +;; 정의되지 않은 변수에 액세스하면 예외가 발생합니다. +s ;; => 오류: 바인딩되지 않은 변수: s + +;; 지역 바인딩 +(let ((me "Bob")) + (print me)) ;; => Bob + +(print me) ;; => 오류: 바인딩되지 않은 변수: me + +;; 이전에 정의된 변수에 새 값을 할당합니다. +(set! myvar 10) +myvar ;; => 10 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 3. 컬렉션 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 쌍 +;; 'cons'는 쌍을 구성하고, +;; 'car'는 첫 번째 요소를 추출하고, 'cdr'은 나머지 요소를 추출합니다. +(cons 'subject 'verb) ;; => '(subject . verb) +(car (cons 'subject 'verb)) ;; => subject +(cdr (cons 'subject 'verb)) ;; => verb + +;; 목록 +;; 두 번째 항목이 목록인 경우 cons는 새 목록을 만듭니다. +(cons 0 '()) ;; => (0) +(cons 1 (cons 2 (cons 3 '()))) +;; => (1 2 3) +;; 'list'는 목록에 대한 편리한 가변 생성자입니다. +(list 1 2 3) ;; => (1 2 3) + + +;; 'append'를 사용하여 목록을 함께 추가합니다. +(append '(1 2) '(3 4)) ;; => (1 2 3 4) + +;; 목록에 대한 일부 기본 작업 +(map add1 '(1 2 3)) ;; => (2 3 4) +(reverse '(1 3 4 7)) ;; => (7 4 3 1) +(sort '(11 22 33 44) >) ;; => (44 33 22 11) + +(define days '(SUN MON FRI)) +(list-ref days 1) ;; => MON +(set! (list-ref days 1) 'TUE) +days ;; => (SUN TUE FRI) + +;; 벡터 +;; 벡터는 요소가 정수로 인덱싱되는 이기종 구조입니다. +;; 벡터는 일반적으로 동일한 길이의 목록보다 공간을 덜 차지합니다. +;; 벡터의 요소에 대한 임의 액세스는 목록보다 빠릅니다. +#(1 2 3) ;; => #(1 2 3) ;; 리터럴 구문 +(vector 'a 'b 'c) ;; => #(a b c) +(vector? #(1 2 3)) ;; => #t +(vector-length #(1 (2) "a")) ;; => 3 +(vector-ref #(1 (2) (3 3)) 2);; => (3 3) + +(define vec #(1 2 3)) +(vector-set! vec 2 4) +vec ;; => #(1 2 4) + +;; 벡터는 목록에서 만들 수 있으며 그 반대도 가능합니다. +(vector->list #(1 2 4)) ;; => '(1 2 4) +(list->vector '(a b c)) ;; => #(a b c) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 4. 함수 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 'lambda'를 사용하여 함수를 만듭니다. +;; 함수는 항상 마지막 표현식의 값을 반환합니다. +(lambda () "Hello World") ;; => # + +;; 함수 정의 주위에 추가 괄호를 사용하여 실행합니다. +((lambda () "Hello World")) ;; => Hello World ;; 인수 목록이 비어 있습니다. + +;; 인수가 있는 함수 +((lambda (x) (* x x)) 3) ;; => 9 +;; 인수가 두 개인 함수 +((lambda (x y) (* x y)) 2 3) ;; => 6 + +;; 변수에 함수 할당 +(define sqr (lambda (x) (* x x))) +sqr ;; => # +(sqr 3) ;; => 9 + +;; 함수 정의 구문 설탕을 사용하여 이것을 단축할 수 있습니다. +(define (sqr x) (* x x)) +(sqr 3) ;; => 9 + +;; 기존 프로시저를 재정의할 수 있습니다. +(foldl cons '() '(1 2 3 4 5)) ;; => (((((() . 1) . 2) . 3) . 4) . 5) +(define (foldl func accu alist) + (if (null? alist) + accu + (foldl func (func (car alist) accu) (cdr alist)))) + +(foldl cons '() '(1 2 3 4 5)) ;; => (5 4 3 2 1) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 5. 같음 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 숫자의 경우 '='를 사용합니다. +(= 3 3.0) ;; => #t +(= 2 1) ;; => #f + +;; 'eq?'는 두 인수가 메모리에서 동일한 객체를 참조하는 경우 #t를 반환합니다. +;; 즉, 간단한 포인터 비교입니다. +(eq? '() '()) +;; => #t ;; 메모리에 빈 목록이 하나만 있습니다. +(eq? (list 3) (list 3)) ;; => #f ;; 동일한 객체가 아닙니다. +(eq? 'yes 'yes) ;; => #t +(eq? 3 3) ;; => #t ;; 이 경우 작동하더라도 이렇게 하지 마십시오. +(eq? 3 3.0) ;; => #f ;; 숫자 비교에는 '='를 사용하는 것이 좋습니다. +(eq? "Hello" "Hello") ;; => #f + +;; 'eqv?'는 숫자와 문자를 제외한 모든 데이터 유형에 대해 'eq?'와 동일합니다. +(eqv? 3 3.0) ;; => #f +(eqv? (expt 2 3) (expt 2 3)) ;; => #t +(eqv? 'yes 'yes) ;; => #t + +;; 'equal?'은 쌍, 벡터 및 문자열의 내용을 재귀적으로 비교하고 +;; 숫자 및 기호와 같은 다른 객체에 eqv?를 적용합니다. +;; 일반적으로 객체가 동일하게 인쇄되면 equal?인 것이 경험 법칙입니다. + +(equal? '(1 2 3) '(1 2 3)) ;; => #t +(equal? #(a b c) #(a b c)) ;; => #t +(equal? 'a 'a) ;; => #t +(equal? "abc" "abc") ;; => #t + +;; 요약: +;; eq?는 객체가 동일한지 테스트합니다. +;; eqv?는 객체가 작동적으로 동등한지 테스트합니다. +;; equal?은 객체가 동일한 구조와 내용을 가지고 있는지 테스트합니다. + +;; 문자열의 같음 비교 +(string=? "Hello" "Hello") ;; => #t + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 6. 제어 흐름 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 조건문 +(if #t ;; 테스트 표현식 + "True" ;; then 표현식 + "False") ;; else 표현식 + ;; => "True" + +(if (> 3 2) + "yes" + "no") ;; => "yes" + +;; 조건문에서 '#f'가 아닌 모든 값은 참으로 처리됩니다. +;; 0, '(), #() ""는 모두 참 값입니다. +(if 0 + "0 is not false" + "0 is false") ;; => "0 is not false" + +;; 'cond'는 일련의 테스트를 연결하고 참 조건을 만나자마자 반환합니다. +;; 'cond'는 'if/elseif/else' 문을 시뮬레이션하는 데 사용할 수 있습니다. +(cond ((> 2 2) "not true so don't return this") + ((< 2 5) "true, so return this") + (else "returning default")) ;; => "true, so return this" + + +;; case 표현식은 다음과 같이 평가됩니다: +;; 키가 평가되고 'eqv?'의 의미에서 각 데이터와 비교됩니다. +;; 일치하는 데이터의 해당 절이 평가되고 결과로 반환됩니다. +(case (* 2 3) ;; 키는 6입니다. + ((2 3 5 7) 'prime) ;; 데이터 1 + ((1 4 6 8) 'composite)) ;; 데이터 2; 일치! + ;; => composite + +;; else 절이 있는 case +(case (car '(c d)) + ((a e i o u) 'vowel) + ((w y) 'semivowel) + (else 'consonant)) ;; => consonant + +;; 부울 표현식 +;; 'and'는 #f로 평가되는 첫 번째 표현식을 반환합니다. +;; 그렇지 않으면 마지막 표현식의 결과를 반환합니다. +(and #t #f (= 2 2.0)) ;; => #f +(and (< 2 5) (> 2 0) "0 < 2 < 5") ;; => "0 < 2 < 5" + +;; 'or'는 #t로 평가되는 첫 번째 표현식을 반환합니다. +;; 그렇지 않으면 마지막 표현식의 결과가 반환됩니다. +(or #f #t #f) ;; => #t +(or #f #f #f) ;; => #f + +;; 'when'은 else 표현식이 없는 'if'와 같습니다. +(when (positive? 5) "I'm positive") ;; => "I'm positive" + +;; 'unless'는 (when (not ) )와 동일합니다. +(unless (null? '(1 2 3)) "not null") ;; => "not null" + + +;; 루프 +;; 루프는 꼬리 재귀의 도움으로 만들 수 있습니다. +(define (loop count) + (unless (= count 0) + (print "hello") + (loop (sub1 count)))) +(loop 4) ;; => hello, hello ... + +;; 또는 명명된 let으로 +(let loop ((i 0) (limit 5)) + (when (< i limit) + (printf "i = ~a\n" i) + (loop (add1 i) limit))) ;; => i = 0, i = 1.... + +;; 'do'는 또 다른 반복 구문입니다. +;; 변수 집합을 초기화하고 각 반복에서 업데이트합니다. +;; 종료 조건이 충족된 후 최종 표현식이 평가됩니다. +(do ((x 0 (add1 x ))) ;; x = 0을 초기화하고 각 반복에서 1을 더합니다. + ((= x 10) (print "done")) ;; 종료 조건 및 최종 표현식 + (print x)) ;; 각 단계에서 실행할 명령 + ;; => 0,1,2,3....9,done + +;; 목록 반복 +(for-each (lambda (a) (print (* a a))) + '(3 5 7)) ;; => 9, 25, 49 + +;; 'map'은 for-each와 같지만 목록을 반환합니다. +(map add1 '(11 22 33)) ;; => (12 23 34) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 7. 확장 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; CHICKEN 코어는 매우 최소화되어 있지만, Eggs라는 라이브러리 확장을 통해 추가 기능이 제공됩니다. +;; 'chicken-install ' 명령으로 Eggs를 설치할 수 있습니다. + +;; 복소수 +3+4i ;; => 3+2i +;; 부정확한 부동 소수점으로 대체되지 않고 분수를 지원합니다. +1/3 ;; => 1/3 +;; bignum을 통해 큰 정수를 지원합니다. +(expt 9 20) ;; => 12157665459056928801 +;; 및 기타 '확장' 함수 +(log 10 (exp 1)) ;; => 2.30258509299405 +(numerator 2/3) ;; => 2 + +;; 'utf8'은 유니코드 지원을 제공합니다. +(import utf8) +"\u03BBx:(\u03BC\u0251.\u0251\u2192\u0251).xx" ;; => "λx:(μɑ.ɑ→ɑ).xx" + +;; 'posix'는 파일 I/O 및 유닉스 계열 운영 체제를 위한 기타 여러 서비스를 제공합니다. +;; 일부 함수는 Windows 시스템에서 사용할 수 없습니다. +;; 자세한 내용은 http://wiki.call-cc.org/man/5/Module%20(chicken%20file%20posix)를 참조하십시오. + +;; 추가할 파일을 열고, "쓰기 전용"으로 열고, 존재하지 않으면 파일을 만듭니다. +(define outfn (file-open "chicken-hen.txt" (+ open/append open/wronly open/creat))) +;; 파일에 일부 텍스트를 씁니다. +(file-write outfn "Did chicken came before hen?") +;; 파일을 닫습니다. +(file-close outfn) +;; 파일을 "읽기 전용"으로 엽니다. +(define infn (file-open "chicken-hen.txt" open/rdonly)) +;; 파일에서 일부 텍스트를 읽습니다. +(file-read infn 30) ;; => ("Did chicken came before hen? ", 28) +(file-close infn) + +;; CHICKEN은 SRFI(Scheme Requests For Implementation) 확장도 지원합니다. +;; CHICKEN에서 지원하는 srfi를 보려면 'http://srfi.schemers.org/srfi-implementers.html"을 참조하십시오. +(import srfi-1) ;; 목록 라이브러리 +(filter odd? '(1 2 3 4 5 6 7)) ;; => (1 3 5 7) +(count even? '(1 2 3 4 5)) ;; => 2 +(take '(12 24 36 48 60) 3) ;; => (12 24 36) +(drop '(12 24 36 48 60) 2) ;; => (36 48 60) +(circular-list 'z 'q) ;; => z q z q ... + +(import srfi-13) ;; 문자열 라이브러리 +(string-reverse "pan") ;; => "nap" +(string-index "Turkey" #\k) ;; => 3 +(string-every char-upper-case? "CHICKEN") ;; => #t +(string-join '("foo" "bar" "baz") ":") ;; => "foo:bar:baz" + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 8. 매크로 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 파이썬과 같은 'for .. in ..' 반복, 목록용 +(define-syntax for + (syntax-rules (in) + ((for elem in alist body ...) + (for-each (lambda (elem) body ...) alist)))) + +(for x in '(2 4 8 16) + (print x)) ;; => 2, 4, 8, 16 + +(for chr in (string->list "PENCHANT") + (print chr)) ;; => P, E, N, C, H, A, N, T + +;; While 루프 +(define-syntax while + (syntax-rules () + ((while cond body ...) + (let loop () + (when cond + body ... + (loop)))))) + +(let ((str "PENCHANT") (i 0)) + (while (< i (string-length str)) ;; while (조건) + (print (string-ref str i)) ;; 본문 + (set! i (add1 i)))) + ;; => P, E, N, C, H, A, N, T + +;; 고급 구문 규칙 입문 -> http://petrofsky.org/src/primer.txt +;; chicken의 매크로 시스템 -> http://lists.gnu.org/archive/html/chicken-users/2008-04/msg00013.html + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 9. 모듈 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; http://wiki.call-cc.org/man/5/Modules도 참조하십시오. + +;; 'test' 모듈은 'hello'라는 값과 'greet'라는 매크로를 내보냅니다. +(module test (hello greet) + (import scheme) + + (define-syntax greet + (syntax-rules () + ((_ whom) + (begin + (display "Hello, ") + (display whom) + (display " !\n") ) ) ) ) + + (define (hello) + (greet "world") ) ) + +;; 별도의 파일(예: test.scm)에 모듈을 정의하고 인터프리터에 로드할 수 있습니다. +;; (load "test.scm") + +;; 모듈 가져오기 +(import test) +(hello) ;; => Hello, world ! +(greet "schemers") ;; => Hello, schemers ! + +;; 다음 명령을 사용하여 모듈 파일을 공유 라이브러리로 컴파일할 수 있습니다. +;; csc -s test.scm +;; (load "test.so") + +;; 펑터 +;; 펑터는 다른 모듈로 매개변수화할 수 있는 상위 수준 모듈입니다. +;; 다음 펑터는 'multiply'라는 함수를 제공하는 'M'이라는 다른 모듈이 필요합니다. +;; 펑터 자체는 제네릭 함수 'square'를 내보냅니다. +(functor (squaring-functor (M (multiply))) (square) + (import scheme M) + (define (square x) (multiply x x))) + +;; 모듈 'nums'는 'squaring-functor'에 매개변수로 전달될 수 있습니다. +(module nums (multiply) + (import scheme) ;; 미리 정의된 모듈 + (define (multiply x y) (* x y))) +;; 최종 모듈은 프로그램에서 가져와 사용할 수 있습니다. +(module number-squarer = (squaring-functor nums)) + +(import number-squarer) +(square 3) ;; => 9 + +;; 다른 입력에 대해 펑터를 인스턴스화할 수 있습니다. +;; 다음은 squaring-functor에 전달할 수 있는 또 다른 예제 모듈입니다. +(module stars (multiply) + (import chicken scheme) ;; 'use' 키워드에 대한 chicken 모듈 + (use srfi-1) ;; 모듈에서 외부 라이브러리를 사용할 수 있습니다. + (define (multiply x y) + (list-tabulate x (lambda _ (list-tabulate y (lambda _ '*)))))) +(module star-squarer = (squaring-functor stars)) + +(import star-squarer) +(square 3) ;; => ((* * *)(* * *)(* * *)) +``` + +## 더 읽을거리 +* [CHICKEN 사용자 설명서](https://wiki.call-cc.org/manual). +* [R5RS 표준](http://www.schemers.org/Documents/Standards/R5RS) + + +## 추가 정보 + +* [다른 언어 프로그래머를 위해](https://wiki.call-cc.org/chicken-for-programmers-of-other-languages) +* [CHICKEN 구문을 다른 언어와 비교](http://plr.sourceforge.net/cgi-bin/plr/launch.py) + +``` \ No newline at end of file diff --git a/ko/citron.md b/ko/citron.md new file mode 100644 index 0000000000..dfab0eab0e --- /dev/null +++ b/ko/citron.md @@ -0,0 +1,213 @@ +--- +name: citron +filename: learncitron.ctr +contributors: + - ["AnotherTest", ""] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```ruby +# 주석은 '#'로 시작합니다. +# 모든 주석은 한 줄을 포함합니다. + +########################################### +## 1. 기본 데이터 유형 및 연산자 +########################################### + +# 숫자가 있습니다. +3. # 3 + +# 숫자는 해석 모드에서 모두 double입니다. + +# 수학 연산자 우선 순위는 존중되지 않습니다. +# 이진 '연산자'는 ltr 순서로 평가됩니다. +1 + 1. # 2 +8 - 4. # 4 +10 + 2 * 3. # 36 + +# 나눗셈은 항상 부동 소수점 나눗셈입니다. +35 / 2 # 17.5. + +# 정수 나눗셈은 간단하지 않으며, floor를 사용할 수 있습니다. +(35 / 2) floor # 17. + +# 부울은 기본 유형입니다. +True. +False. + +# 부울 메시지 +True not. # False +False not. # True +1 = 1. # True +1 !=: 1. # False +1 < 10. # True + +# 여기서 `not`은 `Boolean` 객체에 대한 단항 메시지입니다. +# 메시지는 인스턴스 메서드 호출과 유사합니다. +# 그리고 세 가지 다른 형식이 있습니다: +# 1. 단항 메시지: 길이 > 1이며 인수를 받지 않습니다: + False not. +# 2. 이진 메시지: 길이 = 1이며 단일 인수를 받습니다: + False & True. +# 3. 키워드 메시지: 적어도 하나의 ':'가 있어야 하며, ':' 수만큼 인수를 받습니다. + False either: 1 or: 2. # 2 + +# 문자열 +'This is a string'. +'There are no character types exposed to the user'. +# "You cannot use double quotes for strings" <- 오류 + +# 문자열을 더할 수 있습니다. +'Hello, ' + 'World!'. # 'Hello, World!' + +# 문자열은 문자에 액세스할 수 있습니다. +'This is a beautiful string' at: 0. # 'T' + +########################################### +## 막간: 기본 할당 +########################################### + +# 현재 범위에 값을 할당할 수 있습니다: +var name is value. # `name`에 `value`를 할당합니다. + +# 현재 객체의 네임스페이스에 값을 할당할 수도 있습니다. +my name is value. # 현재 객체의 `name` 속성에 `value`를 할당합니다. + +# 이러한 이름은 컴파일(해석 모드인 경우 구문 분석 읽기) 시 확인되지만 +# 동적 할당으로 처리할 수 있습니다. + +########################################### +## 2. 목록(배열?) 및 튜플 +########################################### + +# 배열은 여러 유형을 가질 수 있습니다. +Array new < 1 ; 2 ; 'string' ; Nil. # Array new < 1 ; 2 ; 'string' ; Nil + +# 튜플은 배열처럼 작동하지만 변경할 수 없습니다. +# 그러나 모든 장난은 배열로 저하시킵니다. +[1, 2, 'string']. # [1, 2, 'string'] + +# 배열과 상호 운용할 수 있습니다. +[1, 'string'] + (Array new < 'wat'). # Array new < 1 ; 'string' ; 'wat' + +# 인덱싱 +[1, 2, 3] at: 1. # 2 + +# 일부 배열 작업 +var arr is Array new < 1 ; 2 ; 3. + +arr head. # 1 +arr tail. # Array new < 2 ; 3. +arr init. # Array new < 1 ; 2. +arr last. # 3 +arr push: 4. # Array new < 1 ; 2 ; 3 ; 4. +arr pop. # 4 +arr pop: 1. # 2, `arr`는 Array new < 1 ; 3.으로 다시 바인딩됩니다. + +# 목록 이해 +[x * 2 + y,, arr, arr + [4, 5],, x > 1]. # Array ← 7 ; 9 ; 10 ; 11 +# 새 변수 이름은 마주칠 때 바인딩되므로 +# `x`는 `arr`의 값에 바인딩되고 +# `y`는 `arr + [4, 5]`의 값에 바인딩됩니다. +# +# 일반적인 형식은 다음과 같습니다: [expr,, bindings*,, predicates*] + + +#################################### +## 3. 함수 +#################################### + +# 두 변수를 사용하는 간단한 함수 +var add is {:a:b ^a + b.}. + +# 이 함수는 형식 인수를 제외한 모든 이름을 +# 호출되는 컨텍스트에서 확인합니다. + +# 함수 사용 +add applyTo: 3 and: 5. # 8 +add applyAll: [3, 5]. # 8 + +# 또한 (사용자 정의 가능한 -- 나중에 자세히 설명) 의사 연산자는 +# 함수 호출에 대한 약어를 허용합니다. +# 기본적으로 REF[args]입니다. + +add[3, 5]. # 8 + +# 이 동작을 사용자 정의하려면 컴파일러 프라그마를 사용하면 됩니다: +#:callShorthand () + +# 그런 다음 지정된 연산자를 사용할 수 있습니다. +# 허용되는 '연산자'는 []{}() 중 하나로만 구성될 수 있습니다. +# 그리고 혼합하여 사용할 수 있습니다(누가 그렇게 하겠습니까?). + +add(3, 5). # 8 + +# 다음과 같은 방식으로 함수를 연산자로 사용할 수도 있습니다: + +3 `add` 5. # 8 +# 이 호출은 다음과 같이 바인딩됩니다: add[(3), 5] +# 기본 고정성은 왼쪽이고 기본 우선 순위는 1이기 때문입니다. + +# 프라그마를 사용하여 이 연산자의 우선 순위/고정성을 변경할 수 있습니다. +#:declare infixr 1 add + +3 `add` 5. # 8 +# 이제 다음과 같이 바인딩됩니다: add[3, (5)]. + +# 또 다른 형태의 함수도 있습니다. +# 지금까지 함수는 동적 방식으로 확인되었습니다. +# 그러나 어휘적으로 범위가 지정된 블록도 가능합니다. +var sillyAdd is {\:x:y add[x,y].}. + +# 이러한 블록에서는 새 변수를 선언할 수 없습니다. +# Object::'letEqual:in:`을 사용하는 경우를 제외하고 +# 그리고 마지막 표현식은 암시적으로 반환됩니다. + +# 람다 표현식에 대한 약어를 사용할 수도 있습니다. +var mul is \:x:y x * y. + +# 이들은 형식 매개변수에 없는 명명된 바인딩을 캡처하고 +# 유지합니다. (참조로) + +########################################### +## 5. 제어 흐름 +########################################### + +# 인라인 조건 표현식 +var citron is 1 = 1 either: 'awesome' or: 'awful'. # citron is 'awesome' + +# 여러 줄도 괜찮습니다. +var citron is 1 = 1 + either: 'awesome' + or: 'awful'. + +# 반복 +10 times: {:x + Pen writeln: x. +}. # 10. -- 부작용: stdout에 10줄, 0부터 9까지의 숫자 포함 + +# Citron은 어휘적으로 범위가 지정된 블록에서 꼬리 호출 재귀를 제대로 지원합니다. +# 따라서 마음껏 사용하십시오. + +# 대부분의 데이터 구조를 매핑하는 것은 `fmap:`만큼 간단합니다. +[1, 2, 3, 4] fmap: \:x x + 1. # [2, 3, 4, 5] + +# `foldl:accumulator:`를 사용하여 목록/튜플을 접을 수 있습니다. +[1, 2, 3, 4] foldl: (\:acc:x acc * 2 + x) accumulator: 4. # 90 + +# 해당 표현식은 다음과 같습니다. +(2 * (2 * (2 * (2 * 4 + 1) + 2) + 3) + 4) + +################################### +## 6. IO +################################### + +# IO는 매우 간단합니다. +# `Pen`은 콘솔 출력에 사용됩니다. +# 그리고 Program::'input' 및 Program::'waitForInput'은 콘솔 입력에 사용됩니다. + +Pen writeln: 'Hello, ocean!' # 터미널에 'Hello, ocean!\n'을 인쇄합니다. + +Pen writeln: Program waitForInput. # 한 줄을 읽고 다시 인쇄합니다. +``` \ No newline at end of file diff --git a/ko/clojure-macros.md b/ko/clojure-macros.md index 189d53912f..459a1ce457 100644 --- a/ko/clojure-macros.md +++ b/ko/clojure-macros.md @@ -3,6 +3,7 @@ contributors: - ["Adam Bard", "http://adambard.com/"] translators: - ["Eunpyoung Kim", "https://github.com/netpyoung"] + - ["Taeyoon Kim", "https://github.com/partrita"] --- 다른 모든 Lisp와 마찬가지로, Clojure가 가진 [동형성(homoiconicity)](https://en.wikipedia.org/wiki/Homoiconic)은 @@ -44,7 +45,7 @@ Clojure랑 친해지면 쉽게 따라갈 수 있습니다. [Clojure in Y Minutes ;; reverse는 더 이상 함수 객체가 아니라 심볼이라는 것에 주목하세요. ;; 매크로는 인자를 받을 수 있습니다. -(defmacro inc2 [arg] +(defmacro inc2 [arg]) (list + 2 arg)) (inc2 2) ; -> 4 @@ -53,13 +54,13 @@ Clojure랑 친해지면 쉽게 따라갈 수 있습니다. [Clojure in Y Minutes ;; 인자도 quote되기 때문입니다. ;; 이를 해결하기 위해, clojure는 매크로를 quote할 수 있는 방법을 제공합니다: `. ;; ` 안에서 ~를 사용하면 외부 스코프에 접근할 수 있습니다. -(defmacro inc2-quoted [arg] +(defmacro inc2-quoted [arg]) `(+ 2 ~arg)) (inc2-quoted 2) ;; destructuring args도 사용할 수 있습니다. ~@를 사용하여 리스트 변수를 확장할 수 있습니다. -(defmacro unless [arg & body] +(defmacro unless [arg & body]) `(if (not ~arg) (do ~@body))) ; do를 빼먹지 마세요! @@ -73,7 +74,7 @@ Clojure랑 친해지면 쉽게 따라갈 수 있습니다. [Clojure in Y Minutes (unless false "Hello") ; -> "Hello" ;; 주의하지 않으면, 매크로는 변수를 덮어쓰는 등 큰 문제를 일으킬 수 있습니다. -(defmacro define-x [] +(defmacro define-x []) '(do (def x 2) (list x))) @@ -85,7 +86,7 @@ Clojure랑 친해지면 쉽게 따라갈 수 있습니다. [Clojure in Y Minutes ;; 이를 피하기 위해, gensym을 이용하여 고유한 식별자를 얻을 수 있습니다. (gensym 'x) ; -> x1281 (혹은 다른 식별자) -(defmacro define-x-safely [] +(defmacro define-x-safely []) (let [sym (gensym 'x)] `(do (def ~sym 2) @@ -96,7 +97,7 @@ Clojure랑 친해지면 쉽게 따라갈 수 있습니다. [Clojure in Y Minutes (list x) ; -> (4) ;; ` 안에서 #를 사용하면 자동으로 각 심볼에 대한 gensym을 생성할 수 있습니다. -(defmacro define-x-hygienically [] +(defmacro define-x-hygienically []) `(do (def x# 2) (list x#))) @@ -108,18 +109,18 @@ Clojure랑 친해지면 쉽게 따라갈 수 있습니다. [Clojure in Y Minutes ;; 매크로를 만들 때는 보통 헬퍼 함수를 많이 이용합니다. ;; 인라인 산술 문법을 지원하는 몇 개의 헬퍼 함수를 만들어 봅시다. (declare inline-2-helper) -(defn clean-arg [arg] +(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]] + [val [op arg]]) (list op val (clean-arg arg))) (defn inline-2-helper - [[arg1 & ops-and-args]] + [[arg1 & ops-and-args]]) (let [ops (partition 2 ops-and-args)] (reduce apply-arg (clean-arg arg1) ops))) @@ -127,7 +128,7 @@ Clojure랑 친해지면 쉽게 따라갈 수 있습니다. [Clojure in Y Minutes (inline-2-helper '(a + (b - 2) - (c * 5))) ; -> (- (+ a (- b 2)) (* c 5)) ; 하지만, 이 함수를 컴파일 타임에 실행하려면 매크로로 만들어야 합니다. -(defmacro inline-2 [form] +(defmacro inline-2 [form]) (inline-2-helper form)) (macroexpand '(inline-2 (1 + (3 / 2) - (1 / 2) + 1))) @@ -139,6 +140,8 @@ Clojure랑 친해지면 쉽게 따라갈 수 있습니다. [Clojure in Y Minutes ### 더 읽어볼거리 -- [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/) +- [매크로 작성하기](http://www.braveclojure.com/writing-macros/) +- [공식 문서](http://clojure.org/macros) +- [언제 매크로를 사용해야 할까요?](https://lispcast.com/when-to-use-a-macro/) + +``` \ No newline at end of file diff --git a/ko/clojure.md b/ko/clojure.md index 85e2fcf19c..9ab0101117 100644 --- a/ko/clojure.md +++ b/ko/clojure.md @@ -3,6 +3,7 @@ contributors: - ["Adam Bard", "http://adambard.com/"] translators: - ["netpyoung", "http://netpyoung.github.io/"] + - ["Taeyoon Kim", "https://github.com/partrita"] --- Clojure는 Java 가상머신을 위해 개발된 Lisp 계통의 언어입니다 @@ -218,7 +219,7 @@ keymap ; => {:a 1, :b 2, :c 3} (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} @@ -377,4 +378,4 @@ Clojuredocs.org는 core 함수들에 대해 다양한 예제와 문서를 보유 [https://4clojure.oxal.org/](https://4clojure.oxal.org/) Clojure-doc.org는 많고 많은 문서들을 보유하고 있습니다: -[http://clojure-doc.org/](http://clojure-doc.org/) +[http://clojure-doc.org/](http://clojure-doc.org/) \ No newline at end of file diff --git a/ko/cmake.md b/ko/cmake.md new file mode 100644 index 0000000000..060aa0667e --- /dev/null +++ b/ko/cmake.md @@ -0,0 +1,162 @@ +--- +category: tool +name: CMake +contributors: + - ["Bruno Alano", "https://github.com/brunoalano"] +filename: CMake +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +CMake는 크로스 플랫폼, 오픈 소스 빌드 시스템입니다. 이 도구를 사용하면 소스 코드를 테스트, 컴파일 및 패키지를 만들 수 있습니다. + +CMake가 해결하려는 문제는 크로스 플랫폼(다른 make 인터프리터는 다른 명령을 가짐)에서 Makefile 및 Autoconfigure 문제와 타사 라이브러리 연결의 용이성입니다. + +CMake는 운영 체제 및 컴파일러에 구애받지 않는 방식으로 빌드 프로세스를 관리하는 확장 가능한 오픈 소스 시스템입니다. 많은 크로스 플랫폼 시스템과 달리 CMake는 네이티브 빌드 환경과 함께 사용하도록 설계되었습니다. 각 소스 디렉토리에 있는 간단한 구성 파일(CMakeLists.txt 파일이라고 함)은 일반적인 방식으로 사용되는 표준 빌드 파일(예: Unix의 makefile 및 Windows MSVC의 프로젝트/작업 공간)을 생성하는 데 사용됩니다. + +```cmake +# CMake에서 이것은 주석입니다. + +# 코드를 실행하려면 다음 명령을 수행하십시오: +# - mkdir build && cd build +# - cmake .. +# - make +# +# 이러한 단계를 통해 하위 디렉토리로 컴파일하는 모범 사례를 따르고 두 번째 줄은 CMake에 새 OS 종속 Makefile을 생성하도록 요청합니다. 마지막으로 네이티브 Make 명령을 실행합니다. + +#------------------------------------------------------------------------------ +# 기본 +#------------------------------------------------------------------------------ +# +# CMake 파일 이름은 "CMakeLists.txt"여야 합니다. + +# Makefile을 생성하는 데 필요한 최소 CMake 버전을 설정합니다. +cmake_minimum_required (VERSION 2.8) + +# 버전이 2.8 미만이면 FATAL_ERROR를 발생시킵니다. +cmake_minimum_required (VERSION 2.8 FATAL_ERROR) + +# 프로젝트 이름을 정의하면 CMake에서 생성된 일부 디렉토리 명명 규칙이 변경됩니다. 두 번째 매개변수로 코드의 LANG을 보낼 수 있습니다. +project (learncmake C) + +# 프로젝트 소스 디렉토리를 설정합니다(규칙일 뿐). +set( LEARN_CMAKE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} ) +set( LEARN_CMAKE_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR} ) + +# `semver` 스타일을 사용하여 빌드 시스템에 현재 코드 버전을 설정하는 것이 유용합니다. +set (LEARN_CMAKE_VERSION_MAJOR 1) +set (LEARN_CMAKE_VERSION_MINOR 0) +set (LEARN_CMAKE_VERSION_PATCH 0) + +# 변수(버전 번호)를 소스 코드 헤더로 보냅니다. +configure_file ( + "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in" + "${PROJECT_BINARY_DIR}/TutorialConfig.h" +) + +# 디렉토리 포함 +# GCC에서 이것은 "-I" 명령을 호출합니다. +include_directories( include ) + +# 추가 라이브러리는 어디에 설치됩니까? 참고: 여기에 포함 경로를 제공하면 후속 확인에서 모든 것이 해결됩니다. +set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake/modules/" ) + +# 조건 +if ( CONDITION ) + # 출력! + + # 부수적인 정보 + message(STATUS "My message") + + # CMake 경고, 처리 계속 + message(WARNING "My message") + + # CMake 경고(dev), 처리 계속 + message(AUTHOR_WARNING "My message") + + # CMake 오류, 처리 계속하지만 생성 건너뛰기 + message(SEND_ERROR "My message") + + # CMake 오류, 처리 및 생성 중지 + message(FATAL_ERROR "My message") +endif() + +if( CONDITION ) + +elseif( CONDITION ) + +else( CONDITION ) + +endif( CONDITION ) + +# 루프 +foreach(loop_var arg1 arg2 ...) + COMMAND1(ARGS ...) + COMMAND2(ARGS ...) + ... +endforeach(loop_var) + +foreach(loop_var RANGE total) +foreach(loop_var RANGE start stop [step]) + +foreach(loop_var IN [LISTS [list1 [...]]] + [ITEMS [item1 [...]]]) + +while(condition) + COMMAND1(ARGS ...) + COMMAND2(ARGS ...) + ... +endwhile(condition) + + +# 논리 연산 +if(FALSE AND (FALSE OR TRUE)) + message("Don't display!") +endif() + +# 일반, 캐시 또는 환경 변수를 지정된 값으로 설정합니다. +# PARENT_SCOPE 옵션이 지정되면 변수는 현재 범위 위의 범위에 설정됩니다. +# `set( ... [PARENT_SCOPE])` + +# 인용 및 비인용 인수 내에서 변수를 참조하는 방법은 무엇입니까? +# 변수 참조는 변수 값 또는 변수가 설정되지 않은 경우 빈 문자열로 대체됩니다. +${variable_name} + +# 목록 +# 소스 파일 목록을 설정합니다. +set( LEARN_CMAKE_SOURCES + src/main.c + src/imagem.c + src/pather.c +) + +# 컴파일러를 호출합니다. +# +# ${PROJECT_NAME}은 Learn_CMake를 참조합니다. +add_executable( ${PROJECT_NAME} ${LEARN_CMAKE_SOURCES} ) + +# 라이브러리를 링크합니다. +target_link_libraries( ${PROJECT_NAME} ${LIBS} m ) + +# 추가 라이브러리는 어디에 설치됩니까? 참고: 여기에 포함 경로를 제공하면 후속 확인에서 모든 것이 해결됩니다. +set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake/modules/" ) + +# 컴파일러 조건 (gcc ; g++) +if ( "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" ) + message( STATUS "Setting the flags for ${CMAKE_C_COMPILER_ID} compiler" ) + add_definitions( --std=c99 ) +endif() + +# OS 확인 +if( UNIX ) + set( LEARN_CMAKE_DEFINITIONS + "${LEARN_CMAKE_DEFINITIONS} -Wall -Wextra -Werror -Wno-deprecated-declarations -Wno-unused-parameter -Wno-comment" ) +endif() +``` + +### 더 많은 자료 + ++ [CMake 튜토리얼](https://cmake.org/cmake-tutorial/) ++ [CMake 문서](https://cmake.org/documentation/) ++ [CMake 마스터하기](http://amzn.com/1930934319/) ++ [현대 CMake 소개](https://cliutils.gitlab.io/modern-cmake/) \ No newline at end of file diff --git a/ko/cobol.md b/ko/cobol.md new file mode 100644 index 0000000000..1555991035 --- /dev/null +++ b/ko/cobol.md @@ -0,0 +1,191 @@ +--- +name: COBOL +contributors: + - ["Hyphz", "http://github.com/hyphz/"] +filename: learn.cob +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +COBOL은 1960년 원래 설계 이후 여러 번 개정된 비즈니스 지향 언어입니다. + +```cobol + *COBOL. 1985년처럼 코딩하기. + *OpenCobolIDE 4.7.6에서 GnuCOBOL로 컴파일됩니다. + + *COBOL은 레거시(COBOL-85)와 + *현대(COBOL-2002 및 COBOL-2014) 버전 간에 상당한 차이가 있습니다. + *레거시 버전은 1-6열을 비워 두어야 합니다(천공 카드의 인덱스 번호를 + *저장하는 데 사용됨). + *7열의 '*'는 주석을 의미합니다. + *레거시 COBOL에서는 주석이 전체 줄만 될 수 있습니다. + *현대 COBOL은 고정 열을 요구하지 않으며 주석에 *>를 사용하며, + *이는 줄 중간에 나타날 수 있습니다. + *레거시 COBOL은 또한 최대 줄 길이에 제한을 둡니다. + *키워드는 레거시 COBOL에서 대문자여야 하지만, + *현대에서는 대소문자를 구분하지 않습니다. + *현대 COBOL은 혼합 대소문자를 사용할 수 있지만 + *COBOL 코드를 작성할 때 여전히 모두 대문자를 사용하는 것이 일반적입니다. + *이것이 대부분의 전문 COBOL 개발자가 하는 일입니다. + *COBOL 문은 마침표로 끝납니다. + + *COBOL 코드는 4개의 부서로 나뉩니다. + *이 부서들은 순서대로 다음과 같습니다: + *IDENTIFICATION DIVISION. + *ENVIRONMENT DIVISION. + *DATA DIVISION. + *PROCEDURE DIVISION. + + *먼저 프로그램에 ID를 부여해야 합니다. + *IDENTIFICATION DIVISION에는 다른 값도 포함될 수 있지만, + *주석일 뿐입니다. PROGRAM-ID는 유일하게 + *필수입니다. + IDENTIFICATION DIVISION. + PROGRAM-ID. LEARN. + AUTHOR. JOHN DOE. + DATE-WRITTEN. 05/02/2020. + + *변수를 선언해 보겠습니다. + *DATA DIVISION 내의 WORKING-STORAGE 섹션에서 이 작업을 수행합니다. + *각 데이터 항목(일명 변수)은 레벨 번호로 시작하고, + *항목 이름, 그 뒤에 변수가 포함할 데이터 유형을 + *설명하는 PICTURE 절이 옵니다. + *거의 모든 COBOL 프로그래머는 PICTURE를 PIC로 축약합니다. + *A는 알파벳, X는 영숫자, 9는 숫자입니다. + + *예: + 01 MYNAME PIC XXXXXXXXXX. *> 10자 문자열. + + *하지만 모든 X를 세는 것은 오류를 유발할 수 있으므로, + *위 코드는 다음과 같이 다시 작성할 수 있습니다. + 01 MYNAME PIC X(10). + + *다음은 몇 가지 추가 예입니다: + 01 AGE PICTURE 9(3). *> 최대 3자리 숫자. + 01 BIRTH_YEAR PIC S9(7). *> 최대 7자리 부호 있는 숫자. + 01 LAST_NAME PIC X(10). *> 최대 10자 문자열. + + *COBOL에서는 여러 공백이 단일 공백과 동일하므로 + *다른 코더가 읽기 쉽도록 코드를 정렬하기 위해 여러 공백을 사용하는 것이 일반적입니다. + + + *이제 코드를 작성해 보겠습니다. 다음은 간단한 Hello World 프로그램입니다. + IDENTIFICATION DIVISION. + PROGRAM-ID. HELLO. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 THE-MESSAGE PIC X(20). + PROCEDURE DIVISION. + DISPLAY "STARTING PROGRAM". + MOVE "HELLO WORLD" TO THE-MESSAGE. + DISPLAY THE-MESSAGE. + STOP RUN. + + *위 코드는 다음을 출력합니다: + *STARTING PROGRAM + *HELLO WORLD + + + + ********COBOL은 수학을 수행할 수 있습니다*************** + ADD 1 TO AGE GIVING NEW-AGE. + SUBTRACT 1 FROM COUNT. + DIVIDE VAR-1 INTO VAR-2 GIVING VAR-3. + COMPUTE TOTAL-COUNT = COUNT1 PLUS COUNT2. + + + *********PERFORM******************** + *PERFORM 키워드를 사용하면 코드의 다른 지정된 섹션으로 점프한 다음 + *지정된 코드 섹션이 완료되면 다음 실행 가능한 문으로 돌아갈 수 있습니다. + *PERFORM이라는 전체 단어를 작성해야 하며 축약할 수 없습니다. + + IDENTIFICATION DIVISION. + PROGRAM-ID. HELLOCOBOL. + + PROCEDURE DIVISION. + FIRST-PARA. + DISPLAY 'THIS IS IN FIRST-PARA'. + *SECOND-PARA를 건너뛰고 3번째 및 4번째를 수행합니다. + *그런 다음 THIRD-PARA 및 FOURTH-PARA를 수행한 후 + *여기로 돌아와 STOP RUN까지 프로그램을 계속합니다. + PERFORM THIRD-PARA THRU FOURTH-PARA. + + SECOND-PARA. + DISPLAY 'THIS IS IN SECOND-PARA'. + + STOP RUN. + + THIRD-PARA. + DISPLAY 'THIS IS IN THIRD-PARA'. + + FOURTH-PARA. + DISPLAY 'THIS IS IN FOURTH-PARA'. + + + *위 프로그램을 컴파일하고 실행하면 + *다음 결과가 생성됩니다(순서 참고): + *THIS IS IN FIRST-PARA + *THIS IS IN THIRD-PARA + *THIS IS IN FOURTH-PARA + *THIS IS IN SECOND-PARA + + + **********STRING을 사용하여 변수 결합 *********** + + *이제 두 가지 관련 COBOL 동사인 STRING과 + *UNSTRING에 대해 알아볼 시간입니다. + + *STRING 동사는 두 개 이상의 문자열을 연결하거나 합치는 데 사용됩니다. + *UNSTRING은 놀랍게도 문자열을 두 개 이상의 작은 문자열로 분리하는 데 사용됩니다. + *프로그램에서 STRING 또는 UNSTRING을 사용할 때 DELIMITED BY를 사용하는 것을 + *기억하는 것이 중요합니다. + + IDENTIFICATION DIVISION. + PROGRAM-ID. LEARNING. + ENVIRONMENT DIVISION. + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 FULL-NAME PIC X(20). + 01 FIRST-NAME PIC X(13) VALUE "BOB GIBBERISH". + 01 LAST-NAME PIC X(5) VALUE "COBB". + PROCEDURE DIVISION. + STRING FIRST-NAME DELIMITED BY SPACE + " " + LAST-NAME DELIMITED BY SIZE + INTO FULL-NAME + END-STRING. + DISPLAY "THE FULL NAME IS: "FULL-NAME. + STOP RUN. + + + *위 코드는 다음을 출력합니다: + *THE FULL NAME IS: BOB COBB + + + *그 이유를 알아보기 위해 살펴보겠습니다. + + *먼저, 문자열 명령으로 생성하는 변수를 포함하여 모든 변수를 + *DATA DIVISION에 선언했습니다. + + *작업은 PROCEDURE DIVISION에서 발생합니다. + *STRING 키워드로 시작하여 END-STRING으로 끝납니다. 그 사이에 + *더 큰 마스터 변수로 결합하려는 것을 나열합니다. 여기서는 + *FIRST-NAME, 공백 및 LAST-NAME을 결합하고 있습니다. + + *FIRST-NAME 및 LAST-NAME 뒤에 오는 DELIMITED BY 구문은 + *프로그램에 각 변수를 얼마나 캡처할지 알려줍니다. + *DELIMITED BY SPACE는 프로그램에 처음부터 시작하여 + *공백을 만날 때까지 변수를 캡처하도록 지시합니다. + *DELIMITED BY SIZE는 프로그램에 변수의 전체 크기를 캡처하도록 지시합니다. + *FIRST-NAME 뒤에 DELIMITED BY SPACE가 있으므로 GIBBERISH + *부분은 무시됩니다. + + *이것을 더 명확하게 하려면 위 코드의 10행을 다음과 같이 변경하십시오. + STRING FIRST-NAME DELIMITED BY SIZE + *그런 다음 프로그램을 다시 실행하십시오. 이번에는 출력이 다음과 같습니다: + *THE FULL NAME IS: BOB GIBBERISH COBB +``` + +## 더 읽을거리 + +* [GnuCOBOL](https://gnucobol.sourceforge.io/) \ No newline at end of file diff --git a/ko/coffeescript.md b/ko/coffeescript.md index 4b3ade4ffc..cdbf31c0ec 100644 --- a/ko/coffeescript.md +++ b/ko/coffeescript.md @@ -3,6 +3,7 @@ contributors: - ["Tenor Biel", "http://github.com/L8D"] translators: - ["wikibook", "http://wikibook.co.kr"] + - ["Taeyoon Kim", "https://github.com/partrita"] --- ``` coffeescript @@ -46,8 +47,7 @@ race = (winner, runners...) -> print winner, runners # 존재 여부 확인: -alert "I knew it!" if elvis? -#=> if(typeof elvis !== "undefined" && elvis !== null) { alert("I knew it!"); } +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) #=> ... diff --git a/ko/coldfusion.md b/ko/coldfusion.md new file mode 100644 index 0000000000..b948a8d96b --- /dev/null +++ b/ko/coldfusion.md @@ -0,0 +1,332 @@ +--- +name: ColdFusion +filename: learncoldfusion.cfm +contributors: + - ["Wayne Boka", "http://wboka.github.io"] + - ["Kevin Morris", "https://twitter.com/kevinmorris"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +ColdFusion은 웹 개발을 위한 스크립팅 언어입니다. +[여기에서 더 읽어보십시오.](http://www.adobe.com/products/coldfusion-family.html) + +### CFML +_**C**old**F**usion **M**arkup **L**anguage_ +ColdFusion은 태그 기반 언어로 시작했습니다. 거의 모든 기능은 태그를 사용하여 사용할 수 있습니다. + +```cfm +HTML 태그는 출력 가독성을 위해 제공되었습니다. + +"로 끝납니다. ---> + + + +

단순 변수

+ +

myVariable을 "myValue"로 설정

+ +

myNumber를 3.14로 설정

+ + + + +

myVariable 표시: #myVariable#

+

myNumber 표시: #myNumber#

+ +
+ +

복합 변수

+ + +

리터럴 또는 대괄호 표기법을 사용하여 myArray1을 1차원 배열로 설정

+ + +

함수 표기법을 사용하여 myArray2를 1차원 배열로 설정

+ + + +

myArray1 내용

+ +

myArray2 내용

+ + + + +

연산자

+

산술

+

1 + 1 = #1 + 1#

+

10 - 7 = #10 - 7#

+

15 * 10 = #15 * 10#

+

100 / 5 = #100 / 5#

+

120 % 5 = #120 % 5#

+

120 mod 5 = #120 mod 5#

+ +
+ + +

비교

+

표준 표기법

+

1은 1과 같습니까? #1 eq 1#

+

15는 1과 같지 않습니까? #15 neq 1#

+

10은 8보다 큽니까? #10 gt 8#

+

1은 2보다 작습니까? #1 lt 2#

+

10은 5보다 크거나 같습니까? #10 gte 5#

+

1은 5보다 작거나 같습니까? #1 lte 5#

+ +

대체 표기법

+

1은 1과 같습니까? #1 eq 1#

+

15는 1과 같지 않습니까? #15 neq 1#

+

10은 8보다 큽니까? #10 gt 8#

+

1은 2보다 작습니까? #1 lt 2#

+

10은 5보다 크거나 같습니까? #10 gte 5#

+

1은 5보다 작거나 같습니까? #1 lte 5#

+ +
+ + +

제어 구조

+ + + +

테스트할 조건: "#myCondition#"

+ + + #myCondition#. 테스트 중입니다. + + #myCondition#. 조심해서 진행하십시오!!! + + myCondition을 알 수 없습니다. + + +
+ + +

루프

+

For 루프

+ +

인덱스는 #i#

+
+ +

For Each 루프 (복합 변수)

+ +

myArray5를 [5, 15, 99, 45, 100]으로 설정

+ + + + +

인덱스는 #i#

+
+ +

myArray4를 ["Alpha", "Bravo", "Charlie", "Delta", "Echo"]로 설정

+ + + + +

인덱스는 #s#

+
+ +

Switch 문

+ +

myArray5를 [5, 15, 99, 45, 100]으로 설정

+ + + + + + +

#i#는 5의 배수입니다.

+
+ +

#i#는 99입니다.

+
+ +

#i#는 5, 15, 45 또는 99가 아닙니다.

+
+
+
+ +
+ +

유형 변환

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
부울로숫자로날짜-시간으로문자열로
"Yes"TRUE1오류"Yes"
"No"FALSE0오류"No"
TRUETRUE1오류"Yes"
FALSEFALSE0오류"No"
숫자숫자가 0이 아니면 True; 그렇지 않으면 False.숫자이 장의 앞부분에 있는 "날짜-시간 값"을 참조하십시오.숫자의 문자열 표현(예: "8").
문자열"Yes"이면 True
"No"이면 False
0으로 변환할 수 있으면 False
다른 숫자로 변환할 수 있으면 True
숫자를 나타내는 경우(예: "1,000" 또는 "12.36E-12") 해당 숫자로 변환됩니다.날짜-시간을 나타내는 경우(다음 열 참조) 해당 날짜-시간 객체의 숫자 값으로 변환됩니다.
ODBC 날짜, 시간 또는 타임스탬프(예: "{ts '2001-06-14 11:30:13'}")이거나 전체 또는 약어 월 이름을 포함한 표준 미국 날짜 또는 시간 형식으로 표현된 경우 해당 날짜-시간 값으로 변환됩니다.
요일 또는 비정상적인 구두점은 오류를 발생시킵니다.
대시, 슬래시 및 공백은 일반적으로 허용됩니다.
문자열
날짜오류날짜-시간 객체의 숫자 값.날짜ODBC 타임스탬프.
+ +
+ +

구성 요소

+ +참조용 코드 (함수는 IE를 지원하기 위해 무언가를 반환해야 합니다) +``` + +```cfs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +sayHello() +

#sayHello()#

+getHello() +

#getHello()#

+getWorld() +

#getWorld()#

+setHello("Hola") +

#setHello("Hola")#

+setWorld("mundo") +

#setWorld("mundo")#

+sayHello() +

#sayHello()#

+getHello() +

#getHello()#

+getWorld() +

#getWorld()#

+``` + +### CFScript +_**C**old**F**usion **S**cript_ +최근 몇 년 동안 ColdFusion 언어는 태그 기능을 반영하는 스크립트 구문을 추가했습니다. 최신 CF 서버를 사용하는 경우 거의 모든 기능을 스크립트 구문을 사용하여 사용할 수 있습니다. + +## 더 읽을거리 + +아래 제공된 링크는 주제를 이해하기 위한 것이므로, Google에서 특정 예제를 자유롭게 검색하십시오. + +1. [Adobe의 Coldfusion 참조](https://helpx.adobe.com/coldfusion/cfml-reference/topics.html) +2. [오픈 소스 문서](http://cfdocs.org/) diff --git a/ko/common-lisp.md b/ko/common-lisp.md index 1a9763574a..1b2e0717de 100644 --- a/ko/common-lisp.md +++ b/ko/common-lisp.md @@ -5,6 +5,7 @@ contributors: - ["Rommel Martinez", "https://ebzzry.io"] translators: - ["Eunpyoung Kim", "https://github.com/netpyoung"] + - ["Taeyoon Kim", "https://github.com/partrita"] --- 커먼 리스프(Common Lisp, CL)는 다양한 산업 어플리케이션에 적합한 범용적인 멀티페러다임 언어입니다. @@ -98,7 +99,7 @@ t ; atom, 참을 나타냅니다 #C(1 2) ; 복소수 ;;; 함수 적용 -;;; (f x y z ...)에서 f는 함수이며, x, y, z, ... 인자입니다. +;;; (f x y z ...)에서 f는 함수이며, x, y, z, ...는 인자입니다. (+ 1 2) ; => 3 @@ -166,7 +167,7 @@ nil ; 거짓; 빈 리스트: () 역시 거짓. ;;;----------------------------------------------------------------------------- ;;; DEFVAR와 DEFPARAMETER를 이용하여 전역 (동적 스코프) 변수를 만들 수 있습니다. -;;; 변수 이름은 다음을 제외한 모든 문자를 사용할 수 있습니다: ()",'`;#|\ +;;; 변수 이름은 다음을 제외한 모든 문자를 사용할 수 있습니다: ()`,'`;#|\ ;;; DEFVAR와 DEFPARAMETER의 차이점으로는, DEFVAR 표현식을 다시 평가하더라도 변수의 값이 변경되지 않는다는 것입니다. ;;; 반면 DEFPARAMETER는 변경됩니다. @@ -189,7 +190,7 @@ nil ; 거짓; 빈 리스트: () 역시 거짓. (let ((me "dance with you")) me) ; => "dance with you" -;;;-----------------------------------------------------------------------------; +;;;------------------------------------------------------------------------------; ;;; 3. 구조체와 컬렉션 ;;;-----------------------------------------------------------------------------; @@ -352,7 +353,7 @@ nil ; 거짓; 빈 리스트: () 역시 거짓. ;;; 반환된 값들을 처리해보겠습니다. (multiple-value-bind (a b) - (gethash 'd *m*) + (gethash 'd *m*) (list a b)) ; => (NIL NIL) @@ -392,7 +393,7 @@ nil ; 거짓; 빈 리스트: () 역시 거짓. (defun hello-world () "Hello World") (hello-world) ; => "Hello World" -;;; 위 정의에서 ()는 인자 리스트입니다. +;;; 위 정의에서 ()는 함수의 인자 리스트입니다. (defun hello (name) (format nil "Hello, ~A" name)) (hello "Steve") ; => "Hello, Steve" @@ -648,9 +649,6 @@ nil ; 거짓; 빈 리스트: () 역시 거짓. (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. @@ -672,4 +670,4 @@ nil ; 거짓; 빈 리스트: () 역시 거짓. - [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/) +- [Lisp Lang](http://lisp-lang.org/) \ No newline at end of file diff --git a/ko/compojure.md b/ko/compojure.md new file mode 100644 index 0000000000..9ffab74be1 --- /dev/null +++ b/ko/compojure.md @@ -0,0 +1,271 @@ +--- +category: tool +name: Compojure +contributors: + - ["Adam Bard", "http://adambard.com/"] +filename: learncompojure.clj +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +## Compojure 시작하기 + +Compojure는 최소한의 노력으로 Clojure에서 *빠르고* *성능이 뛰어난* 웹 애플리케이션을 만들기 위한 DSL입니다: + +```clojure +(ns myapp.core + (:require [compojure.core :refer :all] + [org.httpkit.server :refer [run-server]])) ; httpkit은 서버입니다. + +(defroutes myapp + (GET "/" [] "Hello World")) + +(defn -main [] + (run-server myapp {:port 5000})) +``` + +**1단계:** [Leiningen](http://leiningen.org/)으로 프로젝트를 생성합니다: + +``` +lein new myapp +``` + +**2단계:** 위 코드를 `src/myapp/core.clj`에 넣습니다. + +**3단계:** `project.clj`에 의존성을 추가합니다: + +``` +[compojure "1.1.8"] +[http-kit "2.1.16"] +``` + +**4단계:** 실행: + +``` +lein run -m myapp.core +``` + +다음에서 확인: + +Compojure 앱은 모든 ring 호환 서버에서 실행되지만, 성능과 [대규모 동시성](http://http-kit.org/600k-concurrent-connection-http-kit.html)을 위해 [http-kit](http://http-kit.org/)을 권장합니다. + +### 라우트 + +Compojure에서 각 라우트는 HTTP 메서드와 URL 일치 패턴, 인수 목록 및 본문으로 구성됩니다. + +```clojure +(defroutes myapp + (GET "/" [] "Show something") + (POST "/" [] "Create something") + (PUT "/" [] "Replace something") + (PATCH "/" [] "Modify Something") + (DELETE "/" [] "Annihilate something") + (OPTIONS "/" [] "Appease something") + (HEAD "/" [] "Preview something")) +``` + +Compojure 라우트 정의는 단순히 [요청 맵을 받아 응답 맵을 반환하는](https://github.com/mmcgrana/ring/blob/master/SPEC) 함수입니다: + +```clojure +(myapp {:uri "/" :request-method :post}) +; => {:status 200 +; :headers {"Content-Type" "text/html; charset=utf-8} +; :body "Create Something"} +``` + +본문은 함수일 수 있으며, 요청을 매개변수로 받아야 합니다: + +```clojure +(defroutes myapp + (GET "/" [] (fn [req] "Do something with req"))) +``` + +또는 요청을 직접 사용할 수도 있습니다: + +```clojure +(defroutes myapp + (GET "/" req "Do something with req")) +``` + +라우트 패턴에는 명명된 매개변수가 포함될 수 있습니다: + +```clojure +(defroutes myapp + (GET "/hello/:name" [name] (str "Hello " name))) +``` + +정규식을 제공하여 각 매개변수가 일치하는 것을 조정할 수 있습니다: + +```clojure +(defroutes myapp + (GET ["/file/:name.:ext" :name #".*", :ext #".*"] [name ext] + (str "File: " name ext))) +``` + +### 미들웨어 + +Clojure는 라우팅에 [Ring](https://github.com/ring-clojure/ring)을 사용합니다. +핸들러는 단순히 요청 맵을 받아 응답 맵을 반환하는 함수입니다(Compojure는 문자열을 200 응답으로 변환합니다). + +요청 또는 응답을 수정하기 위해 애플리케이션의 전체 또는 일부를 래핑하는 미들웨어를 쉽게 작성할 수 있습니다: + +```clojure +(defroutes myapp + (GET "/" req (str "Hello World v" (:app-version req)))) + +(defn wrap-version [handler] + (fn [request] + (handler (assoc request :app-version "1.0.1")))) + +(defn -main [] + (run-server (wrap-version myapp) {:port 5000})) +``` + +[Ring-Defaults](https://github.com/ring-clojure/ring-defaults)는 사이트 및 API에 유용한 미들웨어를 제공하므로 의존성에 추가하십시오: + +``` +[ring/ring-defaults "0.1.1"] +``` + +그런 다음 ns에서 가져올 수 있습니다: + +``` +(ns myapp.core + (:require [compojure.core :refer :all] + [ring.middleware.defaults :refer :all] + [org.httpkit.server :refer [run-server]])) +``` + +그리고 `wrap-defaults`를 사용하여 `site-defaults` 미들웨어를 앱에 추가합니다: + +``` +(defn -main [] + (run-server (wrap-defaults myapp site-defaults) {:port 5000})) +``` + +이제 핸들러는 쿼리 매개변수를 사용할 수 있습니다: + +```clojure +(defroutes myapp + (GET "/posts" req + (let [title (get (:params req) :title) + author (get (:params req) :author)] + (str "Title: " title ", Author: " author)))) +``` + +또는 POST 및 PUT 요청의 경우 폼 매개변수도 사용할 수 있습니다. + +```clojure +(defroutes myapp + (POST "/posts" req + (let [title (get (:params req) :title) + author (get (:params req) :author)] + (str "Title: " title ", Author: " author)))) +``` + + +### 반환 값 + +라우트 블록의 반환 값은 HTTP 클라이언트에 전달되거나 Ring 스택의 다음 미들웨어에 전달되는 응답 본문을 결정합니다. 가장 일반적으로 이는 위 예제에서와 같이 문자열입니다. +그러나 [응답 맵](https://github.com/mmcgrana/ring/blob/master/SPEC)을 반환할 수도 있습니다: + +```clojure +(defroutes myapp + (GET "/" [] + {:status 200 :body "Hello World"}) + (GET "/is-403" [] + {:status 403 :body ""}) + (GET "/is-json" [] + {:status 200 :headers {"Content-Type" "application/json"} :body "{}"})) +``` + +### 정적 파일 + +정적 파일을 제공하려면 `compojure.route.resources`를 사용하십시오. +리소스는 프로젝트의 `resources/` 폴더에서 제공됩니다. + +```clojure +(require '[compojure.route :as route]) + +(defroutes myapp + (GET "/") + (route/resources "/")) ; 루트 경로에서 정적 리소스 제공 + +(myapp {:uri "/js/script.js" :request-method :get}) +; => resources/public/js/script.js의 내용 +``` + +### 뷰 / 템플릿 + +Compojure에서 템플릿을 사용하려면 템플릿 라이브러리가 필요합니다. 몇 가지는 다음과 같습니다: + +#### [Stencil](https://github.com/davidsantiago/stencil) + +[Stencil](https://github.com/davidsantiago/stencil)은 [Mustache](http://mustache.github.com/) 템플릿 라이브러리입니다: + +```clojure +(require '[stencil.core :refer [render-string]]) + +(defroutes myapp + (GET "/hello/:name" [name] + (render-string "Hello {{name}}" {:name name}))) +``` + +리소스 디렉토리에서 템플릿을 쉽게 읽을 수 있습니다. 다음은 도우미 함수입니다. + +```clojure +(require 'clojure.java.io) + +(defn read-template [filename] + (slurp (clojure.java.io/resource filename))) + +(defroutes myapp + (GET "/hello/:name" [name] + (render-string (read-template "templates/hello.html") {:name name}))) +``` + +#### [Selmer](https://github.com/yogthos/Selmer) + +[Selmer](https://github.com/yogthos/Selmer)는 Django 및 Jinja2에서 영감을 받은 템플릿 언어입니다: + +```clojure +(require '[selmer.parser :refer [render-file]]) + +(defroutes myapp + (GET "/hello/:name" [name] + (render-file "templates/hello.html" {:name name}))) +``` + +#### [Hiccup](https://github.com/weavejester/hiccup) + +[Hiccup](https://github.com/weavejester/hiccup)은 HTML을 Clojure 코드로 표현하기 위한 라이브러리입니다. + +```clojure +(require '[hiccup.core :as hiccup]) + +(defroutes myapp + (GET "/hello/:name" [name] + (hiccup/html + [:html + [:body + [:h1 {:class "title"} + (str "Hello " name)]]])) +``` + +#### [Markdown](https://github.com/yogthos/markdown-clj) + +[Markdown-clj](https://github.com/yogthos/markdown-clj)는 Markdown 구현입니다. + +```clojure +(require '[markdown.core :refer [md-to-html-string]]) + +(defroutes myapp + (GET "/hello/:name" [name] + (md-to-html-string "## Hello, world"))) +``` + +더 읽을거리: + +* [공식 Compojure 문서](https://github.com/weavejester/compojure/wiki) + +* [용감하고 진실한 Clojure](http://www.braveclojure.com/) \ No newline at end of file diff --git a/ko/coq.md b/ko/coq.md new file mode 100644 index 0000000000..2b1b88cff8 --- /dev/null +++ b/ko/coq.md @@ -0,0 +1,462 @@ +--- +name: Coq +filename: learncoq.v +contributors: + - ["Philip Zucker", "http://www.philipzucker.com/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Coq 시스템은 증명 보조기입니다. 수학적 증명을 구축하고 검증하도록 설계되었습니다. Coq 시스템에는 함수형 프로그래밍 언어 Gallina가 포함되어 있으며 이 언어로 작성된 프로그램의 속성을 증명할 수 있습니다. + +Coq는 종속 타입 언어입니다. 이는 언어의 타입이 변수의 값에 따라 달라질 수 있음을 의미합니다. 이러한 점에서 Agda, Idris, F*, Lean 등과 같은 다른 관련 언어와 유사합니다. Curry-Howard 대응을 통해 프로그램, 속성 및 증명은 동일한 언어로 형식화됩니다. + +Coq는 OCaml로 개발되었으며 OCaml과 일부 구문 및 개념적 유사성을 공유합니다. Coq는 매력적이지만 어려운 주제를 많이 포함하는 언어입니다. 이 튜토리얼은 증명보다는 Coq의 프로그래밍 측면에 초점을 맞출 것입니다. 특히 함수형 프로그래밍에 익숙하지 않은 경우 OCaml을 먼저 배우는 것이 도움이 될 수 있지만 필수는 아닙니다. 이 튜토리얼은 OCaml과 동일한 내용을 기반으로 합니다. + +Coq의 표준 사용 모델은 강력한 REPL처럼 작동하는 대화형 도구 지원을 사용하여 작성하는 것입니다. 두 가지 일반적인 편집기는 CoqIDE 및 Proof General Emacs 모드입니다. + +Proof General 내에서 `Ctrl+C Ctrl+`는 커서까지 평가합니다. + +```coq +(*** 주석 ***) + +(* 주석은 (*와 *)로 묶입니다. 주석을 중첩해도 괜찮습니다. *) + +(* 한 줄 주석은 없습니다. *) + +(*** 변수 및 함수 ***) + +(* Coq 증명 보조기는 Vernacular라는 명령 언어로 제어하고 쿼리할 수 있습니다. Vernacular 키워드는 대문자로 시작하고 명령은 마침표로 끝납니다. 변수 및 함수 선언은 Definition Vernacular로 형성됩니다. *) + +Definition x := 10. + +(* Coq는 때때로 인수의 유형을 추론할 수 있지만, 유형으로 주석을 다는 것이 일반적인 관행입니다. *) + +Definition inc_nat (x : nat) : nat := x + 1. + +(* 정보를 쿼리하기 위한 많은 Vernacular 명령이 존재합니다. 이것들은 매우 유용할 수 있습니다. *) + +Compute (1 + 1). (* 2 : nat *) (* 결과 계산. *) + +Check tt. (* tt : unit *) (* 표현식의 유형 확인 *) + +About plus. (* 객체에 대한 정보 인쇄 *) + +(* 정의를 포함한 정보 인쇄 *) +Print true. (* Inductive bool : Set := true : Bool | false : Bool *) + +Search nat. (* nat 관련 값의 큰 목록 반환 *) +Search "_ + _". (* 패턴으로도 검색할 수 있습니다. *) +Search (?a -> ?a -> bool). (* 패턴은 명명된 매개변수를 가질 수 있습니다. *) +Search (?a * ?a). + +(* Locate는 표기법이 어디에서 왔는지 알려줍니다. 새로운 표기법을 접할 때 매우 유용합니다. *) + +Locate "+". + +(* 함수를 충분한 수의 인수로 호출하지 않으면 오류가 발생하지 않고 새 함수가 생성됩니다. *) +Definition make_inc x y := x + y. (* make_inc는 nat -> nat -> nat입니다. *) +Definition inc_2 := make_inc 2. (* inc_2는 nat -> nat입니다. *) +Compute inc_2 3. (* 5로 평가됩니다. *) + + +(* 정의는 "let ... in" 구문으로 연결할 수 있습니다. 이는 명령형 언어에서 표현식에 사용하기 전에 여러 변수에 값을 할당하는 것과 거의 동일합니다. *) + +Definition add_xy : nat := let x := 10 in + let y := 20 in + x + y. + +(* 패턴 매칭은 명령형 언어의 switch 문과 다소 유사하지만 훨씬 더 표현력이 뛰어납니다. *) + +Definition is_zero (x : nat) := + match x with + | 0 => true + | _ => false (* "_" 패턴은 "다른 모든 것"을 의미합니다. *) + end. + +(* Fixpoint Vernacular를 사용하여 재귀 함수 정의를 정의할 수 있습니다. *) + +Fixpoint factorial n := match n with + | 0 => 1 + | (S n') => n * factorial n' + end. + +(* 함수 적용은 일반적으로 인수에 괄호가 필요하지 않습니다. *) +Compute factorial 5. (* 120 : nat *) + +(* ...인수가 표현식인 경우를 제외하고. *) +Compute factorial (5-1). (* 24 : nat *) + +(* "with"를 사용하여 상호 재귀 함수를 정의할 수 있습니다. *) +Fixpoint is_even (n : nat) : bool := match n with + | 0 => true + | (S n) => is_odd n +end with + is_odd n := match n with + | 0 => false + | (S n) => is_even n + end. + +(* Coq는 완전한 프로그래밍 언어이므로, 프로그램이 종료됨을 이해할 수 있을 때만 프로그램을 허용합니다. 재귀 호출이 입력의 패턴 일치된 하위 부분에 있을 때 가장 쉽게 볼 수 있습니다. 그러면 입력 크기가 항상 감소하기 때문입니다. Coq가 함수가 종료됨을 이해하도록 하는 것은 항상 쉽지 않습니다. 이 주제에 대한 자세한 내용은 문서 끝의 참고 자료를 참조하십시오. *) + +(* 익명 함수는 다음 구문을 사용합니다: *) + +Definition my_square : nat -> nat := fun x => x * x. + +Definition my_id (A : Type) (x : A) : A := x. +Definition my_id2 : forall A : Type, A -> A := fun A x => x. +Compute my_id nat 3. (* 3 : nat *) + +(* Coq에게 밑줄로 용어를 추론하도록 요청할 수 있습니다. *) +Compute my_id _ 3. + +(* 함수의 암시적 인수는 컨텍스트 지식에서 추론할 수 있는 인수입니다. {}로 묶인 매개변수는 기본적으로 암시적입니다. *) + +Definition my_id3 {A : Type} (x : A) : A := x. +Compute my_id3 3. (* 3 : nat *) + +(* 때로는 이것을 끄는 것이 필요할 수 있습니다. @로 모든 인수를 다시 명시적으로 만들 수 있습니다. *) + +Compute @my_id3 nat 3. + +(* 또는 이름으로 인수 제공 *) +Compute my_id3 (A:=nat) 3. + +(* Coq는 OCaml, Haskell 및 Scheme으로 코드를 추출할 수 있습니다. *) +Require Extraction. +Extraction Language OCaml. +Extraction "factorial.ml" factorial. +(* 위는 다음을 포함하는 factorial.ml 및 factorial.mli 파일을 생성합니다: + +type nat = +| O +| S of nat + +(** val add : nat -> nat -> nat **) + +let rec add n m = + match n with + | O -> m + | S p -> S (add p m) + +(** val mul : nat -> nat -> nat **) + +let rec mul n m = + match n with + | O -> O + | S p -> add m (mul p m) + +(** val factorial : nat -> nat **) + +let rec factorial n = match n with +| O -> S O +| S n' -> mul n (factorial n') +*) + + +(*** 표기법 ***) + +(* Coq는 표현식을 더 자연스러운 형태로 작성하는 데 사용할 수 있는 매우 강력한 표기법 시스템을 가지고 있습니다. *) + +Compute Nat.add 3 4. (* 7 : nat *) +Compute 3 + 4. (* 7 : nat *) + +(* 표기법은 프로그램 텍스트에 적용된 구문 변환으로, 평가되기 전에 적용됩니다. 표기법은 표기법 범위로 구성됩니다. 다른 표기법 범위를 사용하면 오버로딩의 약한 개념을 사용할 수 있습니다. *) + +(* 정수 Z와 관련된 정의를 포함하는 Zarith 모듈을 가져옵니다. *) + +Require Import ZArith. + +(* 표기법 범위는 열 수 있습니다. *) +Open Scope Z_scope. + +(* 이제 숫자와 덧셈이 정수에 대해 정의됩니다. *) +Compute 1 + 7. (* 8 : Z *) + +(* 정수 같음 확인 *) +Compute 1 =? 2. (* false : bool *) + +(* Locate는 표기법의 출처와 정의를 찾는 데 유용합니다. *) +Locate "_ =? _". (* Z.eqb x y : Z_scope *) +Close Scope Z_scope. + +(* 이제 nat이 "+"의 기본 해석입니다. *) +Compute 1 + 7. (* 8 : nat *) + +(* 범위는 %를 사용하여 인라인으로 열 수도 있습니다. *) +Compute (3 * -7)%Z. (* -21%Z : Z *) + +(* Coq는 기본적으로 다음 해석 범위를 선언합니다: core_scope, type_scope, function_scope, nat_scope, bool_scope, list_scope, int_scope, uint_scope. ZArith 및 QArith 모듈에 각각 포함된 숫자 범위 Z_scope(정수) 및 Q_scope(분수)도 원할 수 있습니다. *) + +(* 범위 내용을 인쇄할 수 있습니다. *) +Print Scope nat_scope. +(* +Scope nat_scope +Delimiting key is nat +Bound to classes nat Nat.t +"x 'mod' y" := Nat.modulo x y +"x ^ y" := Nat.pow x y +"x ?= y" := Nat.compare x y +"x >= y" := ge x y +"x > y" := gt x y +"x =? y" := Nat.eqb x y +"x a + end. + +(* 패턴 일치가 반박할 수 없는 경우 구조 분해 let을 사용할 수 있습니다. *) +Definition my_fst2 {A B : Type} (x : A * B) : A := let (a,b) := x in + a. + +(*** 목록 ***) + +(* 목록은 cons 및 nil을 사용하거나 list_scope에서 사용할 수 있는 표기법을 사용하여 구축됩니다. *) +Compute cons 1 (cons 2 (cons 3 nil)). (* (1 :: 2 :: 3 :: nil)%list : list nat *) +Compute (1 :: 2 :: 3 :: nil)%list. + +(* ListNotations 모듈에서도 목록 표기법을 사용할 수 있습니다. *) +Require Import List. +Import ListNotations. +Compute [1 ; 2 ; 3]. (* [1; 2; 3] : list nat *) + + +(* 다음을 포함하여 많은 수의 목록 조작 함수를 사용할 수 있습니다: + +• length +• head : 첫 번째 요소 (기본값 포함) +• tail : 첫 번째 요소를 제외한 모든 요소 +• app : 추가 +• rev : 역순 +• nth : n번째 요소 액세스 (기본값 포함) +• map : 함수 적용 +• flat_map : 목록을 반환하는 함수 적용 +• fold_left : 반복자 (머리에서 꼬리까지) +• fold_right : 반복자 (꼬리에서 머리까지) + + *) + +Definition my_list : list nat := [47; 18; 34]. + +Compute List.length my_list. (* 3 : nat *) + +(* Coq의 모든 함수는 총체적이어야 하므로 인덱싱에는 기본값이 필요합니다. *) +Compute List.nth 1 my_list 0. (* 18 : nat *) +Compute List.map (fun x => x * 2) my_list. (* [94; 36; 68] : list nat *) +Compute List.filter (fun x => Nat.eqb (Nat.modulo x 2) 0) my_list. + (* [18; 34] : list nat *) +Compute (my_list ++ my_list)%list. (* [47; 18; 34; 47; 18; 34] : list nat *) + +(*** 문자열 ***) + +Require Import Strings.String. + +(* 문자열 리터럴에는 큰따옴표를 사용합니다. *) +Compute "hi"%string. + +Open Scope string_scope. + +(* 문자열은 "++" 연산자로 연결할 수 있습니다. *) +Compute String.append "Hello " "World". (* "Hello World" : string *) +Compute "Hello " ++ "World". (* "Hello World" : string *) + +(* 문자열은 같음을 비교할 수 있습니다. *) +Compute String.eqb "Coq is fun!" "Coq is fun!". (* true : bool *) +Compute "no" =? "way". (* false : bool *) + +Close Scope string_scope. + +(*** 기타 모듈 ***) + +(* 표준 라이브러리에서 관심 있을 만한 다른 모듈: + +• Logic : 고전 논리 및 종속 같음 +• Arith : 기본 Peano 산술 +• PArith : 기본 양의 정수 산술 +• NArith : 기본 이진 자연수 산술 + +• Numbers : 자연수, 정수 및 주기적 숫자에 대한 다양한 접근 방식 + (현재 공리적으로 및 2^31 이진 워드 위에) +• Bool : 부울 (기본 함수 및 결과) + +• Lists : 단형 및 다형 목록 (기본 함수 및 결과), + 스트림 (공귀납적 유형으로 정의된 무한 시퀀스) +• Sets : 집합 (고전적, 구성적, 유한, 무한, 멱집합 등) +• FSets : 유한 집합 및 유한 맵의 사양 및 구현 + (목록 및 AVL 트리에 의해) +• Reals : 실수 공리화 (고전적, 기본 함수, 정수 부분, 소수 부분, 극한, 미분, 코시 급수, 멱급수 및 결과 등) +• Relations : 관계 (정의 및 기본 결과) +• Sorting : 정렬된 목록 (기본 정의 및 힙 정렬 정확성) +• Strings : 8비트 문자 및 문자열 +• Wellfounded : 잘 정의된 관계 (기본 결과) + *) + +(*** 사용자 정의 데이터 유형 ***) + +(* Coq는 종속적으로 유형이 지정되므로 유형 별칭을 정의하는 것은 값을 위한 별칭을 정의하는 것과 다르지 않습니다. *) + +Definition my_three : nat := 3. +Definition my_nat : Type := nat. + +(* Inductive Vernacular를 사용하여 더 흥미로운 유형을 정의할 수 있습니다. + 간단한 열거형은 다음과 같이 정의할 수 있습니다: *) + +Inductive ml := OCaml | StandardML | Coq. +Definition lang := Coq. (* 유형은 "ml"입니다. *) + +(* 유형 생성자는 비어 있을 필요가 없습니다. *) +Inductive my_number := plus_infinity + | nat_value : nat -> my_number. +Compute nat_value 3. (* nat_value 3 : my_number *) + + +(* 레코드 구문은 튜플과 유사한 유형에 대한 설탕입니다. 구성 요소에 대한 명명된 접근자 함수를 정의합니다. 레코드 유형은 {...} 표기법으로 정의됩니다. *) + +Record Point2d (A : Set) := mkPoint2d { x2 : A ; y2 : A }. +(* 레코드 값은 {|...|} 표기법으로 구성됩니다. *) +Definition mypoint : Point2d nat := {| x2 := 2 ; y2 := 3 |}. +Compute x2 nat mypoint. (* 2 : nat *) +Compute mypoint.(x2 nat). (* 2 : nat *) + +(* 유형은 매개변수화될 수 있습니다. 예를 들어 "아무거나의 목록 목록" 유형에서와 같습니다. 'a는 모든 유형으로 대체될 수 있습니다. *) + +Definition list_of_lists a := list (list a). +Definition list_list_nat := list_of_lists nat. + +(* 유형은 재귀적일 수도 있습니다. 내장된 정수 목록과 유사한 유형에서와 같습니다. *) + +Inductive my_nat_list := + EmptyList | NatList : nat -> my_nat_list -> my_nat_list. + +Compute NatList 1 EmptyList. (* NatList 1 EmptyList : my_nat_list *) + +(** 유형 생성자 일치 **) + +Inductive animal := Dog : string -> animal | Cat : string -> animal. + +Definition say x := + match x with + | Dog x => (x ++ " says woof")%string + | Cat x => (x ++ " says meow")%string + end. + +Compute say (Cat "Fluffy"). (* "Fluffy says meow". *) + +(** 패턴 일치를 사용한 데이터 구조 순회 **) + +(* 재귀 유형은 패턴 일치를 사용하여 쉽게 순회할 수 있습니다. + 내장된 목록 유형의 데이터 구조를 순회하는 방법을 살펴보겠습니다. + 내장된 cons("::")는 중위 연산자처럼 보이지만, + 실제로는 유형 생성자이며 다른 유형과 마찬가지로 일치시킬 수 있습니다. *) +Fixpoint sum_list l := + match l with + | [] => 0 + | head :: tail => head + (sum_list tail) + end. + +Compute sum_list [1; 2; 3]. (* 6으로 평가됩니다. *) + + +(*** 증명 맛보기 ***) + +(* 증명 언어를 설명하는 것은 이 튜토리얼의 범위를 벗어나지만, + 식욕을 돋우기 위한 맛보기입니다. 자세한 내용은 아래 자료를 참조하십시오. *) + +(* 종속 유형 기반 정리 증명기의 매력적인 기능은 증명 언어와 프로그래밍 기능에 동일한 기본 구성 요소가 있다는 것입니다. 예를 들어, 순수 Gallina에서 명제 A와 B가 A를 의미함을 작성하고 증명할 수 있습니다. *) + +Definition my_theorem : forall A B, A /\ B -> A := + fun A B ab => match ab with + | (conj a b) => a + end. + +(* 또는 전술을 사용하여 증명할 수 있습니다. 전술은 증명 용어를 더 자연스러운 스타일로 구축하고 일부 지루한 작업을 자동화하는 데 도움이 되는 매크로 언어입니다. *) + +Theorem my_theorem2 : forall A B, A /\ B -> A. +Proof. + intros A B ab. destruct ab as [ a b ]. apply a. +Qed. + +(* 자동화된 전술 ring을 사용하여 간단한 다항식 등식을 쉽게 증명할 수 있습니다. *) + +Require Import Ring. +Require Import Arith. +Theorem simple_poly : forall (x : nat), (x + 1) * (x + 2) = x * x + 3 * x + 2. + Proof. intros. ring. Qed. + +(* 여기서는 귀납법을 사용하여 1부터 n까지의 모든 숫자의 합에 대한 닫힌 형식을 증명합니다. *) + +Fixpoint sumn (n : nat) : nat := + match n with + | 0 => 0 + | (S n') => n + (sumn n') + end. + +Theorem sum_formula : forall n, 2 * (sumn n) = (n + 1) * n. +Proof. intros n. induction n. + - reflexivity. (* 0 = 0 기본 사례 *) + - simpl. ring [IHn]. (* 귀납 단계 *) +Qed. +``` + +이것은 단지 coq의 겉햝기 입니다. Coq는 흥미롭고 특이한 주제가 많아 현대 연구까지 이어지는 거대한 생태계입니다. + +## 더 읽을거리 + +* [Coq 참조 매뉴얼](https://coq.inria.fr/refman/) +* [소프트웨어 기초](https://softwarefoundations.cis.upenn.edu/) +* [종속 유형을 사용한 인증 프로그래밍](http://adam.chlipala.net/cpdt/) +* [수학적 구성 요소](https://math-comp.github.io/mcb/) +* [Coq'Art: 귀납적 구성의 미적분학](http://www.cse.chalmers.se/research/group/logic/TypesSS05/resources/coq/CoqArt/) +* [FRAP](http://adam.chlipala.net/frap/) \ No newline at end of file diff --git a/ko/crystal.md b/ko/crystal.md new file mode 100644 index 0000000000..c1919587fe --- /dev/null +++ b/ko/crystal.md @@ -0,0 +1,557 @@ +--- +name: Crystal +filename: learncrystal.cr +contributors: + - ["Vitalii Elenhaupt", "http://veelenga.com"] + - ["Arnaud Fernandés", "https://github.com/TechMagister/"] + - ["Valentin Baca", "https://github.com/valbaca/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```crystal +# 이것은 주석입니다. + +# 모든 것은 객체입니다. +nil.class #=> Nil +100.class #=> Int32 +true.class #=> Bool + +# 거짓 값은 nil, false 및 null 포인터입니다. +!nil #=> true : Bool +!false #=> true : Bool +!0 #=> false : Bool + +# 정수 + +1.class #=> Int32 + +# 5가지 부호 있는 정수 유형 +1_i8.class #=> Int8 +1_i16.class #=> Int16 +1_i32.class #=> Int32 +1_i64.class #=> Int64 +1_i128.class #=> Int128 + +# 5가지 부호 없는 정수 유형 +1_u8.class #=> UInt8 +1_u16.class #=> UInt16 +1_u32.class #=> UInt32 +1_u64.class #=> UInt64 +1_u128.class #=> UInt128 + +2147483648.class #=> Int64 +9223372036854775808.class #=> UInt64 + +# 이진수 +0b1101 #=> 13 : Int32 + +# 8진수 +0o123 #=> 83 : Int32 + +# 16진수 +0xFE012D #=> 16646445 : Int32 +0xfe012d #=> 16646445 : Int32 + +# 부동 소수점 + +1.0.class #=> Float64 + +# 두 가지 부동 소수점 유형이 있습니다. +1.0_f32.class #=> Float32 +1_f32.class #=> Float32 + +1e10.class #=> Float64 +1.5e10.class #=> Float64 +1.5e-7.class #=> Float64 + +# 문자는 'a' 쌍의 작은따옴표를 사용합니다. + +'a'.class #=> Char + +# 문자는 32비트 유니코드입니다. +'あ' #=> 'あ' : Char + +# 유니코드 코드 포인트 +'\u0041' #=> 'A' : Char + +# 문자열은 " 쌍의 큰따옴표를 사용합니다. + +"s".class #=> String + +# 문자열은 변경할 수 없습니다. +s = "hello, " #=> "hello, " : String +s.object_id #=> 134667712 : UInt64 +s += "Crystal" +s #=> "hello, Crystal" : String +s.object_id #=> 142528472 : UInt64 + +# 보간 지원 +"sum = #{1 + 2}" #=> "sum = 3" : String + +# 여러 줄 문자열 +"This is + multiline string" #=> "This is\n multiline string" + + +# 큰따옴표가 있는 문자열 +%(hello "world") #=> "hello \"world\"" + +# 기호 +# 변경할 수 없고 재사용 가능한 상수이며 내부적으로 Int32 정수 값으로 표현됩니다. +# 효율적으로 특정 의미 있는 값을 전달하기 위해 문자열 대신 자주 사용됩니다. + +:symbol.class #=> Symbol + +sentence = :question? # :"question?" : Symbol + +sentence == :question? #=> true : Bool +sentence == :exclamation! #=> false : Bool +sentence == "question?" #=> false : Bool + +# 배열 + +[1, 2, 3].class #=> Array(Int32) +[1, "hello", 'x'].class #=> Array(Char | Int32 | String) + +# 빈 배열은 유형을 지정해야 합니다. +[] # 구문 오류: 빈 배열의 경우 '[] of ElementType'을 사용하십시오. +[] of Int32 #=> [] : Array(Int32) +Array(Int32).new #=> [] : Array(Int32) + +# 배열은 인덱싱할 수 있습니다. +array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] : Array(Int32) +array[0] #=> 1 : Int32 +array[10] # IndexError 발생 +array[-6] # IndexError 발생 +array[10]? #=> nil : (Int32 | Nil) +array[-6]? #=> nil : (Int32 | Nil) + +# 끝에서부터 +array[-1] #=> 5 + +# 시작 인덱스와 크기 +array[2, 3] #=> [3, 4, 5] + +# 또는 범위로 +array[1..3] #=> [2, 3, 4] + +# 배열에 추가 +array << 6 #=> [1, 2, 3, 4, 5, 6] + +# 배열 끝에서 제거 +array.pop #=> 6 +array #=> [1, 2, 3, 4, 5] + +# 배열 시작에서 제거 +array.shift #=> 1 +array #=> [2, 3, 4, 5] + +# 배열에 항목이 있는지 확인 +array.includes? 3 #=> true + +# 문자열 배열 및 기호 배열에 대한 특수 구문 +%w(one two three) #=> ["one", "two", "three"] : Array(String) +%i(one two three) #=> [:one, :two, :three] : Array(Symbol) + +# 다른 유형의 해시 리터럴 구문도 있습니다. .new 및 #<< 메서드를 정의하는 한 +set = Set{1, 2, 3} #=> Set{1, 2, 3} +set.class #=> Set(Int32) + +# 위는 다음과 동일합니다. +set = Set(typeof(1, 2, 3)).new #=> Set{} : Set(Int32) +set << 1 #=> Set{1} : Set(Int32) +set << 2 #=> Set{1, 2} : Set(Int32) +set << 3 #=> Set{1, 2, 3} : Set(Int32) + +# 해시 + +{1 => 2, 3 => 4}.class #=> Hash(Int32, Int32) +{1 => 2, 'a' => 3}.class #=> Hash(Char| Int32, Int32) + +# 빈 해시는 유형을 지정해야 합니다. +{} # 구문 오류: 빈 해시의 경우 '{} of KeyType => ValueType'을 사용하십시오. +{} of Int32 => Int32 # {} : Hash(Int32, Int32) +Hash(Int32, Int32).new # {} : Hash(Int32, Int32) + +# 해시는 키로 빠르게 조회할 수 있습니다. +hash = {"color" => "green", "number" => 5} +hash["color"] #=> "green" +hash["no_such_key"] #=> 해시 키 없음: "no_such_key" (KeyError) +hash["no_such_key"]? #=> nil + +# 반환 값의 유형은 모든 키 유형을 기반으로 합니다. +hash["number"] #=> 5 : (Int32 | String) + +# 해시에서 키 존재 여부 확인 +hash.has_key? "color" #=> true + +# 기호 및 문자열 키에 대한 특수 표기법 +{key1: 'a', key2: 'b'} # {:key1 => 'a', :key2 => 'b'} +{"key1": 'a', "key2": 'b'} # {"key1" => 'a', "key2" => 'b'} + +# 다른 유형의 해시 리터럴 구문도 있습니다. .new 및 #[]= 메서드를 정의하는 한 +class MyType + def []=(key, value) + puts "do stuff" + end +end + +MyType{"foo" => "bar"} + +# 위는 다음과 동일합니다. +tmp = MyType.new +tmp["foo"] = "bar" +tmp + +# 범위 + +1..10 #=> Range(Int32, Int32) +Range.new(1, 10).class #=> Range(Int32, Int32) + +# 포함 또는 배타적일 수 있습니다. +(3..5).to_a #=> [3, 4, 5] +(3...5).to_a #=> [3, 4] + +# 범위에 주어진 값이 포함되는지 여부 확인 +(1..8).includes? 2 #=> true + +# 튜플은 고정 크기, 불변, 스택 할당된 값 시퀀스로, 유형이 다를 수 있습니다. +{1, "hello", 'x'}.class #=> Tuple(Int32, String, Char) + +# 튜플의 값을 인덱스로 액세스 +tuple = {:key1, :key2} +tuple[1] #=> :key2 +tuple[2] #=> 오류: Tuple(Symbol, Symbol)에 대한 인덱스 범위를 벗어났습니다(2는 -2..1에 없음). + +# 여러 변수로 확장할 수 있습니다. +a, b, c = {:a, 'b', "c"} +a #=> :a +b #=> 'b' +c #=> "c" + +# Procs는 선택적 컨텍스트(클로저 데이터)가 있는 함수 포인터를 나타냅니다. +# 일반적으로 proc 리터럴로 생성됩니다. +proc = ->(x : Int32) { x.to_s } +proc.class # Proc(Int32, String) +# 또는 새 메서드 사용 +Proc(Int32, String).new { |x| x.to_s } + +# call 메서드로 proc 호출 +proc.call 10 #=> "10" + +# 제어문 + +if true + "if statement" +elsif false + "else-if, optional" +else + "else, also optional" +end + +puts "if as a suffix" if true + +# 표현식으로서의 if +a = if 2 > 1 + 3 + else + 4 + end + +a #=> 3 + +# 삼항 if +a = 1 > 2 ? 3 : 4 #=> 4 + +# Case 문 +cmd = "move" + +action = case cmd + when "create" + "Creating..." + when "copy" + "Copying..." + when "move" + "Moving..." + when "delete" + "Deleting..." +end + +action #=> "Moving..." + +# 루프 +index = 0 +while index <= 3 + puts "Index: #{index}" + index += 1 +end +# Index: 0 +# Index: 1 +# Index: 2 +# Index: 3 + +index = 0 +until index > 3 + puts "Index: #{index}" + index += 1 +end +# Index: 0 +# Index: 1 +# Index: 2 +# Index: 3 + +# 하지만 선호되는 방법은 each를 사용하는 것입니다. +(1..3).each do |index| + puts "Index: #{index}" +end +# Index: 1 +# Index: 2 +# Index: 3 + +# 제어문에서 변수의 유형은 표현식의 유형에 따라 달라집니다. +if a < 3 + a = "hello" +else + a = true +end +typeof(a) #=> (Bool | String) + +if a && b + # 여기서는 a와 b 모두 nil이 아님이 보장됩니다. +end + +if a.is_a? String + a.class #=> String +end + +# 함수 + +def double(x) + x * 2 +end + +# 함수(및 모든 블록)는 암시적으로 마지막 문의 값을 반환합니다. +double(2) #=> 4 + +# 호출이 명확한 경우 괄호는 선택 사항입니다. +double 3 #=> 6 + +double double 3 #=> 12 + +def sum(x, y) + x + y +end + +# 메서드 인수는 쉼표로 구분됩니다. +sum 3, 4 #=> 7 + +sum sum(3, 4), 5 #=> 12 + +# yield +# 모든 메서드에는 암시적이고 선택적인 블록 매개변수가 있습니다. +# 'yield' 키워드로 호출할 수 있습니다. + +def surround + puts '{' + yield + puts '}' +end + +surround { puts "hello world" } + +# { +# hello world +# } + + +# 함수에 블록을 전달할 수 있습니다. +# "&"는 전달된 블록에 대한 참조를 표시합니다. +def guests(&block) + block.call "some_argument" +end + +# 인수의 목록을 전달할 수 있으며, 이는 배열로 변환됩니다. +# 이것이 splat 연산자("*")의 목적입니다. +def guests(*array) + array.each { |guest| puts guest } +end + +# 메서드가 배열을 반환하면 구조 분해 할당을 사용할 수 있습니다. +def foods + ["pancake", "sandwich", "quesadilla"] +end +breakfast, lunch, dinner = foods +breakfast #=> "pancake" +dinner #=> "quesadilla" + +# 관례적으로 부울을 반환하는 모든 메서드는 물음표로 끝납니다. +5.even? # false +5.odd? # true + +# 또한 관례적으로 메서드가 느낌표로 끝나면 수신자를 변경하는 것과 같이 파괴적인 작업을 수행합니다. +# 일부 메서드에는 변경을 수행하는 ! 버전과 +# 새 변경된 버전을 반환하는 비-! 버전이 있습니다. +fruits = ["grapes", "apples", "bananas"] +fruits.sort #=> ["apples", "bananas", "grapes"] +fruits #=> ["grapes", "apples", "bananas"] +fruits.sort! #=> ["apples", "bananas", "grapes"] +fruits #=> ["apples", "bananas", "grapes"] + +# 그러나 일부 변경 메서드는 !로 끝나지 않습니다. +fruits.shift #=> "apples" +fruits #=> ["bananas", "grapes"] + +# class 키워드로 클래스를 정의합니다. +class Human + + # 클래스 변수. 이 클래스의 모든 인스턴스에서 공유됩니다. + @@species = "H. sapiens" + + # 인스턴스 변수. name의 유형은 String입니다. + @name : String + + # 기본 초기화자 + # 인수를 인스턴스의 "name" 인스턴스 변수에 할당합니다. + # 나이가 주어지지 않으면 인수 목록의 기본값으로 대체됩니다. + def initialize(@name, @age = 0) + end + + # 기본 setter 메서드 + def name=(name) + @name = name + end + + # 기본 getter 메서드 + def name + @name + end + + # 위의 기능은 property 메서드를 사용하여 다음과 같이 캡슐화할 수 있습니다. + property :name + + # Getter/setter 메서드는 다음과 같이 개별적으로 만들 수도 있습니다. + getter :name + setter :name + + # 클래스 메서드는 인스턴스 메서드와 구별하기 위해 self를 사용합니다. + # 클래스에서만 호출할 수 있으며 인스턴스에서는 호출할 수 없습니다. + def self.say(msg) + puts msg + end + + def species + @@species + end +end + + +# 클래스 인스턴스화 +jim = Human.new("Jim Halpert") + +dwight = Human.new("Dwight K. Schrute") + +# 몇 가지 메서드를 호출해 보겠습니다. +jim.species #=> "H. sapiens" +jim.name #=> "Jim Halpert" +jim.name = "Jim Halpert II" #=> "Jim Halpert II" +jim.name #=> "Jim Halpert II" +dwight.species #=> "H. sapiens" +dwight.name #=> "Dwight K. Schrute" + +# 클래스 메서드 호출 +Human.say("Hi") #=> Hi를 인쇄하고 nil을 반환합니다. + +# @로 시작하는 변수는 인스턴스 범위입니다. +class TestClass + @var = "I'm an instance var" +end + +# @@로 시작하는 변수는 클래스 범위입니다. +class TestClass + @@var = "I'm a class var" +end +# 대문자로 시작하는 변수는 상수입니다. +Var = "I'm a constant" +Var = "can't be updated" # 오류: 이미 초기화된 상수 Var + +# 클래스도 Crystal의 객체입니다. 따라서 클래스는 인스턴스 변수를 가질 수 있습니다. +# 클래스 변수는 클래스와 모든 하위 클래스에서 공유됩니다. + +# 기본 클래스 +class Human + @@foo = 0 + + def self.foo + @@foo + end + + def self.foo=(value) + @@foo = value + end +end + +# 파생 클래스 +class Worker < Human +end + +Human.foo #=> 0 +Worker.foo #=> 0 + +Human.foo = 2 #=> 2 +Worker.foo #=> 0 + +Worker.foo = 3 #=> 3 +Human.foo #=> 2 +Worker.foo #=> 3 + +module ModuleExample + def foo + "foo" + end +end + +# 모듈을 포함하면 해당 메서드가 클래스 인스턴스에 바인딩됩니다. +# 모듈을 확장하면 해당 메서드가 클래스 자체에 바인딩됩니다. + +class Person + include ModuleExample +end + +class Book + extend ModuleExample +end + +Person.foo # => Person:Class에 대한 정의되지 않은 메서드 'foo' +Person.new.foo # => 'foo' +Book.foo # => 'foo' +Book.new.foo # => Book에 대한 정의되지 않은 메서드 'foo' + + +# 예외 처리 + +# 새 예외 정의 +class MyException < Exception +end + +# 다른 예외 정의 +class MyAnotherException < Exception; end + +ex = begin + raise MyException.new +rescue ex1 : IndexError + "ex1" +rescue ex2 : MyException | MyAnotherException + "ex2" +rescue ex3 : Exception + "ex3" +rescue ex4 # 모든 종류의 예외를 잡습니다. + "ex4" +end + +ex #=> "ex2" +``` + +## 추가 자료 + +- [공식 문서](https://crystal-lang.org/) \ No newline at end of file diff --git a/ko/csharp.md b/ko/csharp.md new file mode 100644 index 0000000000..0824878d45 --- /dev/null +++ b/ko/csharp.md @@ -0,0 +1,1216 @@ +--- +name: C# +contributors: + - ["Irfan Charania", "https://github.com/irfancharania"] + - ["Max Yankov", "https://github.com/golergka"] + - ["Melvyn Laïly", "http://x2a.yt"] + - ["Shaun McCarthy", "http://www.shaunmccarthy.com"] + - ["Wouter Van Schandevijl", "http://github.com/laoujin"] + - ["Jo Pearce", "http://github.com/jdpearce"] + - ["Chris Zimmerman", "https://github.com/chriszimmerman"] + - ["Shawn McGuire", "https://github.com/bigbash"] +filename: LearnCSharp.cs +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +C#은 개발자가 크로스 플랫폼 .NET 프레임워크에서 실행되는 다양하고 안전하며 견고한 애플리케이션을 구축할 수 있도록 하는 우아하고 타입 안전한 객체 지향 언어입니다. + +[여기에서 더 읽어보십시오.](https://docs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/) + +```csharp +// 한 줄 주석은 //로 시작합니다. + +/* +여러 줄 주석은 이렇습니다. +*/ + +/// +/// 이것은 외부 문서를 생성하거나 IDE 내에서 컨텍스트 도움말을 제공하는 데 사용할 수 있는 XML 문서 주석입니다. +/// +/// 이것은 firstParam에 대한 매개변수 문서입니다. +/// 함수의 반환 값에 대한 정보 +public void MethodOrClassOrOtherWithParsableHelp(string firstParam) { } + +// 이 소스 코드가 사용할 네임스페이스를 지정합니다. +// 아래 네임스페이스는 모두 표준 .NET Framework 클래스 라이브러리의 일부입니다. +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using System.IO; + +// 하지만 이것은 아닙니다: +using System.Data.Entity; +// 사용하려면 dll 참조를 추가해야 합니다. +// NuGet 패키지 관리자를 사용하여 수행할 수 있습니다: `Install-Package EntityFramework` + +// 네임스페이스는 코드를 "패키지" 또는 "모듈"로 구성하기 위한 범위를 정의합니다. +// 다른 소스 파일에서 이 코드를 사용하려면: using Learning.CSharp; + +// C# 10에서도 이 작업을 수행할 수 있습니다. 파일 범위 네임스페이스라고 합니다. +// namespace Learning.CSharp; + +namespace Learning.CSharp +{ + // 각 .cs 파일에는 파일과 동일한 이름을 가진 클래스가 하나 이상 포함되어야 합니다. + // 그렇지 않아도 되지만, 정신 건강을 위해 그렇게 하지 않는 것이 좋습니다. + public class LearnCSharp + { + // 기본 구문 - 이전에 Java 또는 C++를 사용해 본 적이 있다면 흥미로운 기능으로 건너뛰십시오. + public static void Syntax() + { + // Console.WriteLine을 사용하여 줄을 인쇄합니다. + Console.WriteLine("Hello World"); + Console.WriteLine( + "Integer: " + 10 + + " Double: " + 3.14 + + " Boolean: " + true); + + // 새 줄 없이 인쇄하려면 Console.Write를 사용하십시오. + Console.Write("Hello "); + Console.Write("World"); + + /////////////////////////////////////////////////// + // 유형 및 변수 + // + // 을 사용하여 변수를 선언합니다. + /////////////////////////////////////////////////// + + // Sbyte - 부호 있는 8비트 정수 + // (-128 <= sbyte <= 127) + sbyte fooSbyte = 100; + + // Byte - 부호 없는 8비트 정수 + // (0 <= byte <= 255) + byte fooByte = 100; + + // Short - 16비트 정수 + // 부호 있는 - (-32,768 <= short <= 32,767) + // 부호 없는 - (0 <= ushort <= 65,535) + short fooShort = 10000; + ushort fooUshort = 10000; + + // Integer - 32비트 정수 + int fooInt = 1; // (-2,147,483,648 <= int <= 2,147,483,647) + uint fooUint = 1; // (0 <= uint <= 4,294,967,295) + + // Long - 64비트 정수 + long fooLong = 100000L; // (-9,223,372,036,854,775,808 <= long <= 9,223,372,036,854,775,807) + ulong fooUlong = 100000L; // (0 <= ulong <= 18,446,744,073,709,551,615) + // 숫자는 크기에 따라 int 또는 uint로 기본 설정됩니다. + // L은 이 변수 값이 long 또는 ulong 유형임을 나타내는 데 사용됩니다. + + // Double - 배정밀도 64비트 IEEE 754 부동 소수점 + double fooDouble = 123.4; // 정밀도: 15-16자리 + + // Float - 단정밀도 32비트 IEEE 754 부동 소수점 + float fooFloat = 234.5f; // 정밀도: 7자리 + // f는 이 변수 값이 float 유형임을 나타내는 데 사용됩니다. + + // Decimal - 128비트 데이터 유형으로, 다른 부동 소수점 유형보다 정밀도가 높으며, + // 금융 및 통화 계산에 적합합니다. + decimal fooDecimal = 150.3m; + + // Boolean - true & false + bool fooBoolean = true; // 또는 false + + // Char - 단일 16비트 유니코드 문자 + char fooChar = 'A'; + + // 문자열 -- 이전 기본 유형은 모두 값 유형이지만, + // 문자열은 참조 유형입니다. 즉, null로 설정할 수 있습니다. + string fooString = "\"escape\" quotes and add \n (new lines) and \t (tabs)"; + Console.WriteLine(fooString); + + // 인덱서를 사용하여 문자열의 각 문자에 액세스할 수 있습니다: + char charFromString = fooString[1]; // => 'e' + // 문자열은 변경할 수 없습니다: fooString[1] = 'X';를 수행할 수 없습니다. + + // 현재 문화권으로 문자열 비교, 대소문자 구분 안 함 + string.Compare(fooString, "x", StringComparison.CurrentCultureIgnoreCase); + + // sprintf를 기반으로 한 형식 지정 + string fooFs = string.Format("Check Check, {0} {1}, {0} {1:0.0}", 1, 2); + + // 날짜 및 형식 지정 + DateTime fooDate = DateTime.Now; + Console.WriteLine(fooDate.ToString("hh:mm, dd MMM yyyy")); + + // 문자 그대로의 문자열 + // 문자열 리터럴 앞에 @ 기호를 사용하여 문자열의 모든 문자를 이스케이프할 수 있습니다. + string path = "C:\\Users\\User\\Desktop"; + string verbatimPath = @"C:\Users\User\Desktop"; + Console.WriteLine(path == verbatimPath); // => true + + // @ 기호를 사용하여 문자열을 두 줄로 나눌 수 있습니다. "를 이스케이프하려면 ""를 사용하십시오. + string bazString = @"Here's some stuff +on a new line! ""Wow!"", the masses cried"; + + // 변수를 변경할 수 없게 만들려면 const 또는 read-only를 사용하십시오. + // const 값은 컴파일 타임에 계산됩니다. + const int HoursWorkPerWeek = 9001; + + /////////////////////////////////////////////////// + // 데이터 구조 + /////////////////////////////////////////////////// + + // 배열 - 0부터 인덱싱 + // 배열 크기는 선언 시 결정되어야 합니다. + // 배열을 선언하는 형식은 다음과 같습니다. + // [] = new []; + int[] intArray = new int[10]; + + // 배열을 선언하고 초기화하는 또 다른 방법 + int[] y = { 9000, 1000, 1337 }; + + // 배열 인덱싱 - 요소 액세스 + Console.WriteLine("intArray @ 0: " + intArray[0]); + // 배열은 변경 가능합니다. + intArray[1] = 1; + + // 목록 + // 목록은 더 유연하므로 배열보다 더 자주 사용됩니다. + // 목록을 선언하는 형식은 다음과 같습니다. + // List = new List(); + List intList = new List(); + List stringList = new List(); + List z = new List { 9000, 1000, 1337 }; // 초기화 + // <>는 제네릭용입니다 - 멋진 기능 섹션을 확인하십시오. + + // 목록은 기본적으로 값을 가지지 않습니다. + // 인덱스에 액세스하기 전에 값을 추가해야 합니다. + intList.Add(1); + Console.WriteLine("intList at 0: " + intList[0]); + + // 확인할 다른 데이터 구조: + // 스택/큐 + // 사전(해시 맵 구현) + // HashSet + // 읽기 전용 컬렉션 + // 튜플(.NET 4+) + + /////////////////////////////////////// + // 연산자 + /////////////////////////////////////// + Console.WriteLine("\n->연산자"); + + int i1 = 1, i2 = 2; // 여러 선언에 대한 약어 + + // 산술은 간단합니다. + Console.WriteLine(i1 + i2 - i1 * 3 / 7); // => 3 + + // 모듈로 + Console.WriteLine("11%3 = " + (11 % 3)); // => 2 + + // 비교 연산자 + Console.WriteLine("3 == 2? " + (3 == 2)); // => false + Console.WriteLine("3 != 2? " + (3 != 2)); // => true + Console.WriteLine("3 > 2? " + (3 > 2)); // => true + Console.WriteLine("3 < 2? " + (3 < 2)); // => false + Console.WriteLine("2 <= 2? " + (2 <= 2)); // => true + Console.WriteLine("2 >= 2? " + (2 >= 2)); // => true + + // 비트 연산자! + /* + ~\t단항 비트 보수 + <<\t부호 있는 왼쪽 시프트 + >>\t부호 있는 오른쪽 시프트 + &\t비트 AND + ^\t비트 배타적 OR + |\t비트 포함 OR + */ + + // 증가 + int i = 0; + Console.WriteLine("\n->증감"); + Console.WriteLine(i++); // "0" 인쇄, i = 1. 후위 증가 + Console.WriteLine(++i); // "2" 인쇄, i = 2. 전위 증가 + Console.WriteLine(i--); // "2" 인쇄, i = 1. 후위 감소 + Console.WriteLine(--i); // "0" 인쇄, i = 0. 전위 감소 + + /////////////////////////////////////// + // 제어 구조 + /////////////////////////////////////// + Console.WriteLine("\n->제어 구조"); + + // If 문은 C와 유사합니다. + int j = 10; + if (j == 10) + { + Console.WriteLine("I get printed"); + } + else if (j > 10) + { + Console.WriteLine("I don't"); + } + else + { + Console.WriteLine("I also don't"); + } + + // 삼항 연산자 + // 간단한 if/else는 다음과 같이 작성할 수 있습니다. + // ? : + int toCompare = 17; + string isTrue = toCompare == 17 ? "True" : "False"; + + // While 루프 + int fooWhile = 0; + while (fooWhile < 100) + { + // 100번 반복, fooWhile 0->99 + fooWhile++; + } + + // Do While 루프 + int fooDoWhile = 0; + do + { + // 100번 반복 시작, fooDoWhile 0->99 + if (false) + continue; // 현재 반복 건너뛰기 + + fooDoWhile++; + + if (fooDoWhile == 50) + break; // 루프에서 완전히 벗어나기 + + } while (fooDoWhile < 100); + + // for 루프 구조 => for(; ; ) + for (int fooFor = 0; fooFor < 10; fooFor++) + { + // 10번 반복, fooFor 0->9 + } + + // For Each 루프 + // foreach 루프 구조 => foreach( in ) + // foreach 루프는 IEnumerable 또는 IEnumerable를 구현하는 모든 객체를 반복합니다. + // .NET 프레임워크의 모든 컬렉션 유형(Array, List, Dictionary...)은 이러한 인터페이스 중 하나 또는 둘 다를 구현합니다. + // (ToCharArray()는 문자열도 IEnumerable을 구현하므로 제거할 수 있습니다.) + foreach (char character in "Hello World".ToCharArray()) + { + // 문자열의 모든 문자를 반복합니다. + } + + // 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; + // 여러 케이스를 하나의 작업에 할당할 수 있습니다. + // 그러나 다른 케이스 앞에 break 없이 작업을 추가할 수 없습니다. + // (이렇게 하려면 명시적으로 goto case x를 추가해야 합니다.) + case 6: + case 7: + case 8: + monthString = "Summer time!!"; + break; + default: + monthString = "Some other month"; + break; + } + + /////////////////////////////////////// + // 데이터 유형 변환 및 유형 캐스팅 + /////////////////////////////////////// + + // 데이터 변환 + + // 문자열을 정수로 변환 + // 실패 시 FormatException을 throw합니다. + int.Parse("123"); // "123"의 정수 버전을 반환합니다. + + // TryParse는 실패 시 유형의 기본값으로 기본 설정됩니다. + // 이 경우 0입니다. + int tryInt; + if (int.TryParse("123", out tryInt)) // 함수는 부울입니다. + Console.WriteLine(tryInt); // 123 + + // 정수를 문자열로 변환 + // Convert 클래스에는 변환을 용이하게 하는 여러 메서드가 있습니다. + + // 문자열을 int로 + + // 더 나은 방법 + bool result = int.TryParse(string, out var integer) + int.Parse(string); + + // 권장하지 않음 + Convert.ToString(123); + + // Int를 문자열로 + tryInt.ToString(); + + // 캐스팅 + // 십진수 15를 int로 캐스팅 + // 그런 다음 long으로 암시적으로 캐스팅 + long x = (int) 15M; + } + + /////////////////////////////////////// + // 클래스 - 파일 끝의 정의 참조 + /////////////////////////////////////// + public static void Classes() + { + // 파일 끝의 객체 선언 참조 + + // new를 사용하여 클래스를 인스턴스화합니다. + Bicycle trek = new Bicycle(); + + // 객체 메서드 호출 + trek.SpeedUp(3); // 항상 setter 및 getter 메서드를 사용해야 합니다. + trek.Cadence = 100; + + // ToString은 이 객체의 값을 표시하는 규칙입니다. + Console.WriteLine("trek info: " + trek.Info()); + + // 새 Penny Farthing 인스턴스화 + PennyFarthing funbike = new PennyFarthing(1, 10); + Console.WriteLine("funbike info: " + funbike.Info()); + + Console.Read(); + } // 주 메서드 끝 + + // C# 9 이상에서 사용 가능하며, 기본적으로 클래스에 대한 구문 설탕입니다. 레코드는 불변입니다*. + public record ARecord(string Csharp); + + // 콘솔 진입 - 콘솔 애플리케이션은 진입점으로 main 메서드를 가져야 합니다. + public static void Main(string[] args) + { + OtherInterestingFeatures(); + } + + // + // 흥미로운 기능 + // + + // 기본 메서드 시그니처 + + public // 가시성 + static // 객체 없이 클래스에서 직접 호출 가능 + int // 반환 유형, + MethodSignatures( + int maxCount, // 첫 번째 변수, int 예상 + int count = 0, // 전달되지 않으면 값을 0으로 기본 설정 + int another = 3, + params string[] otherParams // 메서드에 전달된 다른 모든 매개변수 캡처 + ) + { + return -1; + } + + // 메서드는 시그니처가 고유한 한 동일한 이름을 가질 수 있습니다. + // 반환 유형만 다른 메서드는 고유하지 않습니다. + public static void MethodSignatures( + ref int maxCount, // 참조로 전달 + out int count) + { + // 'count'로 전달된 인수는 이 함수 외부에서 15의 값을 가집니다. + count = 15; // out 매개변수는 제어가 메서드를 떠나기 전에 할당되어야 합니다. + } + + // 제네릭 + // TKey 및 TValue에 대한 클래스는 이 함수를 호출하는 사용자가 지정합니다. + // 이 메서드는 Python의 dict.setdefault()를 에뮬레이트합니다. + public static TValue SetDefault( + IDictionary dictionary, + TKey key, + TValue defaultItem) + { + TValue result; + if (!dictionary.TryGetValue(key, out result)) + return dictionary[key] = defaultItem; + return result; + } + + // 위에서 제네릭으로 정의된 SETDEFAULT를 호출합니다. + // 암시적으로 파생될 수 있으므로 TKey 및 TValue를 지정할 필요가 없습니다. + Console.WriteLine(SetDefault(phonebook, "Shaun", "No Phone")); // 전화 없음 + // 참고, TKey 및 TValue를 지정할 필요가 없습니다. 암시적으로 파생될 수 있기 때문입니다. + Console.WriteLine(SetDefault(phonebook, "Sarah", "No Phone")); // 212 555 5555 + + // 람다 표현식 - 인라인으로 코드를 작성할 수 있습니다. + Func square = (x) => x * x; // 마지막 T 항목은 반환 값입니다. + Console.WriteLine(square(3)); // 9 + + // 오류 처리 - 불확실한 세상에 대처하기 + try + { + var funBike = PennyFarthing.CreateWithGears(6); + + // CreateWithGears가 예외를 throw하므로 더 이상 실행되지 않습니다. + string some = ""; + if (true) some = null; + some.ToLower(); // NullReferenceException을 throw합니다. + } + catch (NotSupportedException) + { + Console.WriteLine("Not so much fun now!"); + } + catch (Exception ex) // 다른 모든 예외를 잡습니다. + { + throw new ApplicationException("It hit the fan", ex); + // throw; // 호출 스택을 보존하는 다시 throw + } + // catch { } // 예외를 캡처하지 않는 catch-all + finally + { + // try 또는 catch 후에 실행됩니다. + } + + // 일회용 리소스 관리 - 관리되지 않는 리소스를 쉽게 처리할 수 있습니다. + // 관리되지 않는 리소스(파일 핸들, 장치 컨텍스트 등)에 액세스하는 대부분의 객체는 IDisposable 인터페이스를 구현합니다. + // using 문은 이러한 IDisposable 객체를 정리하는 것을 처리합니다. + using (StreamWriter writer = new StreamWriter("log.txt")) + { + writer.WriteLine("Nothing suspicious here"); + // 범위 끝에서 리소스가 해제됩니다. + // 예외가 발생하더라도. + } + + // 병렬 프레임워크 + // https://devblogs.microsoft.com/csharpfaq/parallel-programming-in-net-framework-4-getting-started/ + + var words = new List {"dog", "cat", "horse", "pony"}; + + Parallel.ForEach(words, + new ParallelOptions() { MaxDegreeOfParallelism = 4 }, + word => + { + Console.WriteLine(word); + } + ); + + // 이것을 실행하면 다른 출력이 생성됩니다. + // 각 스레드가 다른 시간에 완료되기 때문입니다. + // 몇 가지 예제 출력은 다음과 같습니다: + // cat dog horse pony + // dog horse pony cat + + // 동적 객체(다른 언어와 함께 작업하는 데 좋습니다.) + dynamic student = new ExpandoObject(); + student.FirstName = "First Name"; // 먼저 클래스를 정의할 필요가 없습니다! + + // 메서드를 추가할 수도 있습니다(문자열을 반환하고 문자열을 받습니다). + student.Introduce = new Func( + (introduceTo) => string.Format("Hey {0}, this is {1}", student.FirstName, introduceTo)); + Console.WriteLine(student.Introduce("Beth")); + + // IQUERYABLE - 거의 모든 컬렉션이 이를 구현하므로 매우 유용한 Map / Filter / Reduce 스타일 메서드를 많이 사용할 수 있습니다. + var bikes = new List(); + bikes.Sort(); // 배열 정렬 + bikes.Sort((b1, b2) => b1.Wheels.CompareTo(b2.Wheels)); // 바퀴를 기준으로 정렬 + var result = bikes + .Where(b => b.Wheels > 3) // 필터 - 연결 가능(이전 유형의 IQueryable 반환) + .Where(b => b.IsBroken && b.HasTassles) + .Select(b => b.ToString()); // 맵 - 이것만 선택하므로 결과는 IQueryable입니다. + + var sum = bikes.Sum(b => b.Wheels); // Reduce - 컬렉션의 모든 바퀴를 합산합니다. + + // 자전거의 일부 매개변수를 기반으로 암시적 객체 목록을 만듭니다. + var bikeSummaries = bikes.Select(b=>new { Name = b.Name, IsAwesome = !b.IsBroken && b.HasTassles }); + // 여기서 보여주기는 어렵지만, 컴파일러가 위에서 유형을 암시적으로 파악할 수 있으므로 유형 자동 완성 기능을 사용할 수 있습니다! + foreach (var bikeSummary in bikeSummaries.Where(b => b.IsAwesome)) + Console.WriteLine(bikeSummary.Name); + + // ASPARALLEL + // 그리고 여기서부터 상황이 복잡해집니다 - linq와 병렬 작업을 결합합니다. + var threeWheelers = bikes.AsParallel().Where(b => b.Wheels == 3).Select(b => b.Name); + // 이것은 병렬로 발생합니다! 스레드가 자동으로 생성되고 결과가 그들 사이에 분할됩니다! 많은 코어가 있는 대규모 데이터 세트에 놀랍습니다. + + // LINQ - 저장소를 IQueryable 객체에 매핑하고 지연 실행합니다. + // 예: LinqToSql - 데이터베이스에 매핑, LinqToXml은 xml 문서에 매핑 + var db = new BikeRepository(); + + // 실행이 지연되므로 데이터베이스 쿼리에 좋습니다. + var filter = db.Bikes.Where(b => b.HasTassles); // 쿼리 실행 안 됨 + if (42 > 6) // 필터를 계속 추가할 수 있으며, 조건부로도 가능합니다 - "고급 검색" 기능에 좋습니다. + filter = filter.Where(b => b.IsBroken); // 쿼리 실행 안 됨 + + var query = filter + .OrderBy(b => b.Wheels) + .ThenBy(b => b.Name) + .Select(b => b.Name); // 여전히 쿼리 실행 안 됨 + + // 이제 쿼리가 실행되지만 리더를 열므로 반복할 때만 채워집니다. + foreach (string bike in query) + Console.WriteLine(result); + + + + } + + } // LearnCSharp 클래스 끝 + + // .cs 파일에 다른 클래스를 포함할 수 있습니다. + + public static class Extensions + { + // 확장 메서드 + public static void Print(this object obj) + { + Console.WriteLine(obj.ToString()); + } + } + + + // 대리자 및 이벤트 + public class DelegateTest + { + public static int count = 0; + public static int Increment() + { + // count를 증가시킨 다음 반환합니다. + return ++count; + } + + // 대리자는 메서드에 대한 참조입니다. + // Increment 메서드를 참조하려면 먼저 동일한 시그니처를 가진 대리자를 선언해야 합니다. + // 즉, 인수를 받지 않고 int를 반환합니다. + public delegate int IncrementDelegate(); + + // 이벤트도 대리자를 트리거하는 데 사용할 수 있습니다. + // 대리자 유형으로 이벤트를 만듭니다. + public static event IncrementDelegate MyEvent; + + static void Main(string[] args) + { + // 대리자를 인스턴스화하고 메서드 자체를 인수로 전달하여 Increment 메서드를 참조합니다. + IncrementDelegate inc = new IncrementDelegate(Increment); + Console.WriteLine(inc()); // => 1 + + // 대리자는 + 연산자로 구성할 수 있습니다. + IncrementDelegate composedInc = inc; + composedInc += inc; + composedInc += inc; + + // composedInc는 Increment를 3번 실행합니다. + Console.WriteLine(composedInc()); // => 4 + + + // 대리자로 이벤트 구독 + MyEvent += new IncrementDelegate(Increment); + MyEvent += new IncrementDelegate(Increment); + + // 이벤트 트리거 + // 즉, 이 이벤트에 구독된 모든 대리자를 실행합니다. + Console.WriteLine(MyEvent()); // => 6 + } + } + + + // 클래스 선언 구문: + // class <클래스 이름>{ + // //데이터 필드, 생성자, 함수 모두 내부에 있습니다. + // //함수는 Java에서 메서드라고 불립니다. + // } + + public class Bicycle + { + // Bicycle의 필드/변수 + public int Cadence // Public: 어디서든 액세스할 수 있습니다. + { + get // get - 속성을 검색하는 메서드 정의 + { + return _cadence; + } + set // set - 속성을 설정하는 메서드 정의 + { + _cadence = value; // Value는 setter에 전달된 값입니다. + } + } + private int _cadence; + + protected virtual int Gear // Protected: 클래스 및 하위 클래스에서 액세스할 수 있습니다. + { + get; // 멤버 필드가 필요 없는 자동 속성을 만듭니다. + set; + } + + internal int Wheels // Internal: 어셈블리 내에서 액세스할 수 있습니다. + { + get; + private set; // getter/setter 메서드에 접근 한정자를 설정할 수 있습니다. + } + + int _speed; // 모든 것은 기본적으로 private입니다: 이 클래스 내에서만 액세스할 수 있습니다. + // private 키워드도 사용할 수 있습니다. + public string Name { get; set; } + + // 속성에는 단순히 표현식의 결과를 반환하는 읽기 전용 속성을 원하는 경우에 대한 특수 구문도 있습니다. + public string LongName => Name + " " + _speed + " speed"; + + // Enum은 명명된 상수 집합으로 구성된 값 유형입니다. + // 기본적으로 이름을 값(달리 지정되지 않는 한 int)에 매핑하는 것입니다. + // enum에 대해 승인된 유형은 byte, sbyte, short, ushort, int, uint, long 또는 ulong입니다. + // enum은 동일한 값을 두 번 포함할 수 없습니다. + public enum BikeBrand + { + AIST, + BMC, + Electra = 42, //이름에 명시적으로 값을 설정할 수 있습니다. + Gitane // 43 + } + // 이 유형을 Bicycle 클래스 내에 정의했으므로 중첩된 유형입니다. + // 이 클래스 외부의 코드는 이 유형을 Bicycle.BikeBrand로 참조해야 합니다. + + public BikeBrand Brand; // enum 유형을 선언한 후 이 유형의 필드를 선언할 수 있습니다. + + // FlagsAttribute로 enum을 장식하여 여러 값을 전환할 수 있음을 나타냅니다. + // Attribute에서 파생된 모든 클래스는 유형, 메서드, 매개변수 등을 장식하는 데 사용할 수 있습니다. + // 비트 연산자 & 및 |를 사용하여 AND/OR 연산을 수행할 수 있습니다. + + [Flags] + public enum BikeAccessories + { + None = 0, + Bell = 1, + MudGuards = 2, // 값을 수동으로 설정해야 합니다! + Racks = 4, + Lights = 8, + FullPackage = Bell | MudGuards | Racks | Lights + } + + // 사용법: aBike.Accessories.HasFlag(Bicycle.BikeAccessories.Bell) + // .NET 4 이전: (aBike.Accessories & Bicycle.BikeAccessories.Bell) == Bicycle.BikeAccessories.Bell + public BikeAccessories Accessories { get; set; } + + // 정적 멤버는 특정 객체가 아닌 유형 자체에 속합니다. + // 참조 없이 액세스할 수 있습니다: + // Console.WriteLine("Bicycles created: " + Bicycle.bicyclesCreated); + public static int BicyclesCreated { get; set; } + + // 읽기 전용 값은 런타임에 설정됩니다. + // 선언 시 또는 생성자에서만 할당할 수 있습니다. + readonly bool _hasCardsInSpokes = false; // 읽기 전용 private + + // 생성자는 클래스를 만드는 방법입니다. + // 이것은 기본 생성자입니다. + public Bicycle() + { + this.Gear = 1; // this 키워드로 객체의 멤버에 액세스할 수 있습니다. + Cadence = 50; // 그러나 항상 필요한 것은 아닙니다. + _speed = 5; + Name = "Bontrager"; + Brand = BikeBrand.AIST; + BicyclesCreated++; + } + + // 이것은 지정된 생성자입니다(인수를 포함합니다). + public Bicycle(int startCadence, int startSpeed, int startGear, + string name, bool hasCardsInSpokes, BikeBrand brand) + : base() // 먼저 base를 호출합니다. + { + Gear = startGear; + Cadence = startStartCadence; + _speed = startSpeed; + Name = name; + _hasCardsInSpokes = hasCardsInSpokes; + Brand = brand; + } + + // 생성자는 연결될 수 있습니다. + public Bicycle(int startCadence, int startSpeed, BikeBrand brand) : + this(startCadence, startSpeed, 0, "big wheels", true, brand) + { + } + + // 함수 구문: + // <반환 유형> <함수 이름>(<인수>) + + // 클래스는 필드에 대한 getter 및 setter를 구현할 수 있습니다. + // 또는 속성을 구현할 수 있습니다(이것이 C#에서 선호되는 방법입니다). + + // 메서드 매개변수는 기본값을 가질 수 있습니다. + // 이 경우 이러한 매개변수를 생략하고 메서드를 호출할 수 있습니다. + public void SpeedUp(int increment = 1) + { + _speed += increment; + } + + public void SlowDown(int decrement = 1) + { + _speed -= decrement; + } + + // 속성은 값을 가져오거나 설정합니다. + // 데이터에만 액세스해야 하는 경우 속성 사용을 고려하십시오. + // 속성은 get 또는 set 또는 둘 다를 가질 수 있습니다. + private bool _hasTassles; // private 변수 + public bool HasTassles // public 접근자 + { + get { return _hasTassles; } + set { _hasTassles = value; } + } + + // 속성은 자동으로 구현될 수 있습니다. + // 이 구문은 자동으로 백킹 필드를 생성합니다. + // getter 또는 setter(또는 둘 다)에 접근 한정자를 설정하여 접근을 제한할 수 있습니다: + public bool IsBroken { get; private set; } + + // 속성은 자동으로 구현될 수 있습니다. + public int FrameSize + { + get; + // getter 또는 setter에 대한 접근 한정자를 지정할 수 있습니다. + // 이는 Bicycle 클래스만 Framesize에 set을 호출할 수 있음을 의미합니다. + private set; + } + + // 객체에 사용자 정의 인덱서를 정의하는 것도 가능합니다. + // 이 예제에서는 완전히 유용하지 않지만, + // bicycle[0]을 사용하여 첫 번째 승객을 얻거나 + // bicycle[1] = "lisa"를 사용하여 승객을 설정할 수 있습니다(이 명백한 쿼트로사이클의 경우). + private string[] passengers = { "chris", "phil", "darren", "regina" }; + + public string this[int i] + { + get { + return passengers[i]; + } + + set { + passengers[i] = value; + } + } + + // 이 객체의 속성 값을 표시하는 메서드입니다. + public virtual string Info() + { + return "Gear: " + Gear + + " Cadence: " + Cadence + + " Speed: " + _speed + + " Name: " + Name + + " Cards in Spokes: " + (_hasCardsInSpokes ? "yes" : "no") + + "\n------------------------------\n" + ; + } + + // 메서드도 정적일 수 있습니다. 도우미 메서드에 유용할 수 있습니다. + public static bool DidWeCreateEnoughBicycles() + { + // 정적 메서드 내에서는 정적 클래스 멤버만 참조할 수 있습니다. + return BicyclesCreated > 9000; + } // 클래스에 정적 멤버만 필요한 경우 클래스 자체를 정적으로 표시하는 것을 고려하십시오. + + + } // Bicycle 클래스 끝 + + // PennyFarthing은 Bicycle의 하위 클래스입니다. + class PennyFarthing : Bicycle + { + // (페니 파딩은 앞바퀴가 굉장히 큰 자전거입니다. + // 기어가 없습니다.) + + // 부모 생성자 호출 + public PennyFarthing(int startCadence, int startSpeed) : + base(startCadence, startSpeed, 0, "PennyFarthing", true, BikeBrand.Electra) + { + } + + protected override int Gear + { + get + { + return 0; + } + set + { + throw new InvalidOperationException("You can't change gears on a PennyFarthing"); + } + } + + public static PennyFarthing CreateWithGears(int gears) + { + var penny = new PennyFarthing(1, 1); + penny.Gear = gears; // 이런, 이렇게 할 수 없습니다! + return penny; + } + + public override string Info() + { + string result = "PennyFarthing bicycle "; + result += base.ToString(); // 메서드의 기본 버전 호출 + return result; + } + } + + // 인터페이스는 구현 없이 멤버의 시그니처만 포함합니다. + interface IJumpable + { + void Jump(int meters); // 모든 인터페이스 멤버는 암시적으로 public입니다. + } + + interface IBreakable + { + bool Broken { get; } // 인터페이스는 메서드 및 이벤트뿐만 아니라 속성도 포함할 수 있습니다. + } + + // 클래스는 다른 클래스를 하나만 상속할 수 있지만, 여러 인터페이스를 구현할 수 있습니다. + // 그러나 기본 클래스 이름은 목록의 첫 번째에 있어야 하며 모든 인터페이스는 그 뒤에 와야 합니다. + class MountainBike : Bicycle, IJumpable, IBreakable + { + int damage = 0; + + public void Jump(int meters) + { + damage += meters; + } + + public bool Broken + { + get + { + return damage > 100; + } + } + } + + /// + /// LinqToSql 예제를 위한 DB 연결에 사용됩니다. + /// EntityFramework Code First는 훌륭합니다(Ruby의 ActiveRecord와 유사하지만 양방향). + /// https://docs.microsoft.com/ef/ef6/modeling/code-first/workflows/new-database + /// + public class BikeRepository : DbContext + { + public BikeRepository() + : base() + { + } + + public DbSet Bikes { get; set; } + } + + // 클래스는 여러 .cs 파일로 분할될 수 있습니다. + // A1.cs + public partial class A + { + public static void A1() + { + Console.WriteLine("Method A1 in class A"); + } + } + + // A2.cs + public partial class A + { + public static void A2() + { + Console.WriteLine("Method A2 in class A"); + } + } + + // 부분 클래스 "A"를 사용하는 프로그램 + public class Program + { + static void Main() + { + A.A1(); + A.A2(); + } + } + + // 문자열 보간은 문자열 앞에 $를 붙이고 보간하려는 표현식을 { 중괄호 }로 묶습니다. + // 보간된 문자열과 문자 그대로의 문자열을 $@로 결합할 수도 있습니다. + public class Rectangle + { + public int Length { get; set; } + public int Width { get; set; } + } + + class Program + { + static void Main(string[] args) + { + Rectangle rect = new Rectangle { Length = 5, Width = 3 }; + Console.WriteLine($"The length is {rect.Length} and the width is {rect.Width}"); + + string username = "User"; + Console.WriteLine($@"C:\\Users\\{username}\\Desktop"); + } + } + + // 새 C# 6 기능 + class GlassBall : IJumpable, IBreakable + { + // 자동 속성 초기화자 + public int Damage { get; private set; } = 0; + + // getter 전용 속성 초기화자 + public string Name { get; } = "Glass ball"; + + // 생성자에서 초기화되는 getter 전용 자동 속성 + public string GenieName { get; } = ""; + + public GlassBall(string genieName = null) + { + GenieName = genieName; + } + + public void Jump(int meters) + { + if (meters < 0) + // 새로운 nameof() 표현식; 컴파일러는 식별자가 존재하는지 확인합니다. + // nameof(x) == "x" + // 예를 들어 매개변수 이름이 변경되지만 오류 메시지에서 업데이트되지 않는 것을 방지합니다. + throw new ArgumentException("Cannot jump negative amount!", nameof(meters)); + + Damage += meters; + } + + // 표현식 본문 속성 ... + public bool Broken + => Damage > 100; + + // ... 및 메서드 + public override string ToString() + // 보간된 문자열 + => $"{Name}. Damage taken: {Damage}"; + + public string SummonGenie() + // null 조건부 연산자 + // x?.y는 x가 null이면 즉시 null을 반환합니다; y는 평가되지 않습니다. + => GenieName?.ToUpper(); + } + + static class MagicService + { + private static bool LogException(Exception ex) + { + // 예외를 어딘가에 기록합니다. + return false; + } + + public static bool CastSpell(string spell) + { + try + { + // 여기에 API를 호출한다고 가정합니다. + throw new MagicServiceException("Spell failed", 42); + + // 주문 성공 + return true; + } + // Code가 42인 경우에만 잡습니다. 즉, 주문이 실패했습니다. + catch(MagicServiceException ex) when (ex.Code == 42) + { + // 주문 실패 + return false; + } + // 다른 예외 또는 Code가 42가 아닌 MagicServiceException + catch(Exception ex) when (LogException(ex)) + { + // 이 블록은 실행되지 않습니다. + // 스택은 해제되지 않습니다. + } + return false; + // 참고: MagicServiceException을 잡고 Code가 42 또는 117이 아닌 경우 다시 throw하는 것은 다릅니다. 그러면 최종 catch-all 블록이 다시 throw된 예외를 잡지 않습니다. + } + } + + public class MagicServiceException : Exception + { + public int Code { get; } + + public MagicServiceException(string message, int code) : base(message) + { + Code = code; + } + } + + public static class PragmaWarning { + // Obsolete 속성 + [Obsolete("Use NewMethod instead", false)] + public static void ObsoleteMethod() + { + // 더 이상 사용되지 않는 코드 + } + + public static void NewMethod() + { + // 새 코드 + } + + public static void Main() + { + ObsoleteMethod(); // CS0618: 'ObsoleteMethod는 더 이상 사용되지 않습니다: NewMethod를 대신 사용하십시오.' +#pragma warning disable CS0618 + ObsoleteMethod(); // 경고 없음 +#pragma warning restore CS0618 + ObsoleteMethod(); // CS0618: 'ObsoleteMethod는 더 이상 사용되지 않습니다: NewMethod를 대신 사용하십시오.' + } + } +} + +using System; +// C# 6, static using +using static System.Math; + +namespace Learning.More.CSharp +{ + class StaticUsing + { + static void Main() + { + // static using 문 없이... + Console.WriteLine("The square root of 4 is {0}.", Math.Sqrt(4)); + // static using 문 사용 + Console.WriteLine("The square root of 4 is {0}.", Sqrt(4)); + } + } +} + +// 새 C# 7 기능 +// Nuget에서 Microsoft.Net.Compilers 최신 버전 설치 +// Nuget에서 System.ValueTuple 최신 버전 설치 +using System; +namespace Csharp7 +{ + // 튜플, 구조 분해 및 무시 + class TuplesTest + { + public (string, string) GetName() + { + // 튜플의 필드는 기본적으로 Item1, Item2...로 명명됩니다. + var names1 = ("Peter", "Parker"); + Console.WriteLine(names1.Item2); // => Parker + + // 필드는 대신 명시적으로 명명될 수 있습니다. + // 유형 1 선언 + (string FirstName, string LastName) names2 = ("Peter", "Parker"); + + // 유형 2 선언 + var names3 = (First:"Peter", Last:"Parker"); + + Console.WriteLine(names2.FirstName); // => Peter + Console.WriteLine(names3.Last); // => Parker + + return names3; + } + + public string GetLastName() { + var fullName = GetName(); + + // 튜플은 구조 분해될 수 있습니다. + (string firstName, string lastName) = fullName; + + // 구조 분해된 튜플의 필드는 _를 사용하여 무시될 수 있습니다. + var (_, last) = fullName; + return last; + } + + // 모든 유형은 Deconstruct 메서드를 지정하여 동일한 방식으로 구조 분해될 수 있습니다. + public int randomNumber = 4; + public int anotherRandomNumber = 10; + + public void Deconstruct(out int randomNumber, out int anotherRandomNumber) + { + randomNumber = this.randomNumber; + anotherRandomNumber = this.anotherRandomNumber; + } + + static void Main(string[] args) + { + var tt = new TuplesTest(); + (int num1, int num2) = tt; + Console.WriteLine($"num1: {num1}, num2: {num2}"); // => num1: 4, num2: 10 + + Console.WriteLine(tt.GetLastName()); + } + } + + // 패턴 매칭 + class PatternMatchingTest + { + public static (string, int)? CreateLogMessage(object data) + { + switch(data) + { + // when을 사용한 추가 필터링 + case System.Net.Http.HttpRequestException h when h.Message.Contains("404"): + return (h.Message, 404); + case System.Net.Http.HttpRequestException h when h.Message.Contains("400"): + return (h.Message, 400); + case Exception e: + return (e.Message, 500); + case string s: + return (s, s.Contains("Error") ? 500 : 200); + case null: + return null; + default: + return (data.ToString(), 500); + } + } + } + + // 참조 로컬 + // 객체에 대한 참조를 반환할 수 있도록 합니다(값만 반환하는 대신). + class RefLocalsTest + { + // 반환에서 참조를 참고하십시오. + public static ref string FindItem(string[] arr, string el) + { + for(int i=0; i apple + } + } + + // 로컬 함수 + class LocalFunctionTest + { + private static int _id = 0; + public int id; + public LocalFunctionTest() + { + id = generateId(); + + // 이 로컬 함수는 이 범위에서만 액세스할 수 있습니다. + int generateId() + { + return _id++; + } + } + + public static void AnotherMethod() + { + var lf1 = new LocalFunctionTest(); + var lf2 = new LocalFunctionTest(); + Console.WriteLine($"{lf1.id}, {lf2.id}"); // => 0, 1 + + int id = generateId(); + // 오류 CS0103: 현재 컨텍스트에 'generateId'라는 이름이 없습니다. + } + } +} +``` \ No newline at end of file diff --git a/ko/css.md b/ko/css.md new file mode 100644 index 0000000000..58a3cf4874 --- /dev/null +++ b/ko/css.md @@ -0,0 +1,360 @@ +--- +name: CSS +contributors: + - ["Mohammad Valipour", "https://github.com/mvalipour"] + - ["Marco Scannadinari", "https://github.com/marcoms"] + - ["Geoffrey Liu", "https://github.com/g-liu"] + - ["Connor Shea", "https://github.com/connorshea"] + - ["Deepanshu Utkarsh", "https://github.com/duci9y"] + - ["Brett Taylor", "https://github.com/glutnix"] + - ["Tyler Mumford", "https://tylermumford.com"] +filename: learncss.css +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +웹 페이지는 HTML로 구축되며, HTML은 페이지의 내용을 지정합니다. +CSS(Cascading Style Sheets)는 페이지의 **모양**을 지정하는 별도의 언어입니다. + +CSS 코드는 정적 *규칙*으로 구성됩니다. 각 규칙은 하나 이상의 *선택자*를 사용하고 +여러 시각적 *속성*에 특정 *값*을 부여합니다. 이러한 속성은 +선택자가 나타내는 페이지 요소에 적용됩니다. + +이 가이드는 CSS2를 염두에 두고 작성되었으며, CSS3의 새로운 기능으로 확장되었습니다. + +**참고:** CSS는 시각적 결과를 생성하므로, 배우려면 [dabblet](http://dabblet.com/)과 같은 CSS 플레이그라운드에서 모든 것을 시도해야 합니다. +이 문서의 주요 초점은 구문과 몇 가지 일반적인 팁에 있습니다. + +## 구문 + +```css +/* 주석은 슬래시-별표 안에 나타납니다. 이 줄처럼요! + "한 줄 주석"은 없습니다. 이것이 유일한 주석 스타일입니다. */ + +/* #################### + ## 선택자 + #################### */ + +/* 선택자는 페이지의 요소를 대상으로 하는 데 사용됩니다. */ +selector { property: value; /* 더 많은 속성... */ } + +/* +다음은 예제 요소입니다: + +
+*/ + +/* CSS 클래스 중 하나를 사용하여 대상을 지정할 수 있습니다. */ +.class1 { } + +/* 또는 두 클래스 모두! */ +.class1.class2 { } + +/* 또는 이름 */ +div { } + +/* 또는 ID */ +#anID { } + +/* 또는 속성이 있다는 사실을 사용하여! */ +[attr] { font-size:smaller; } + +/* 또는 속성이 특정 값을 가집니다. */ +[attr='value'] { font-size:smaller; } + +/* 값으로 시작합니다 (CSS 3) */ +[attr^='val'] { font-size:smaller; } + +/* 또는 값으로 끝납니다 (CSS 3) */ +[attr$='ue'] { font-size:smaller; } + +/* 또는 값을 포함합니다 (CSS 3) */ +[attr*='foo'] { } + +/* 또는 공백으로 구분된 목록에 값을 포함합니다. */ +[otherAttr~='foo'] { } +[otherAttr~='bar'] { } + +/* 또는 대시로 구분된 목록에 값을 포함합니다. 예: "-" (U+002D) */ +[otherAttr|='en'] { font-size:smaller; } + + +/* 다른 선택자를 결합하여 더 집중된 선택자를 만들 수 있습니다. 그 사이에 공백을 두지 마십시오. */ +div.some-class[attr$='ue'] { } + +/* 다른 요소의 자식인 요소를 선택할 수 있습니다. */ +div.some-parent > .class-name { } + +/* 또는 다른 요소의 후손. 자식은 부모 요소의 직접적인 후손이며, 트리에서 한 레벨만 아래에 있습니다. 후손은 트리에서 어떤 레벨이든 될 수 있습니다. */ +div.some-parent .class-name { } + +/* 경고: 공백이 없는 동일한 선택자는 다른 의미를 가집니다. + 무엇인지 추측할 수 있습니까? */ +div.some-parent.class-name { } + +/* 인접 형제를 기반으로 요소를 선택할 수도 있습니다. */ +.i-am-just-before + .this-element { } + +/* 또는 그 앞에 오는 모든 형제 */ +.i-am-any-element-before ~ .this-element { } + +/* 특정 상태에 있을 때만 요소를 선택하는 데 사용할 수 있는 의사 클래스라는 선택자가 있습니다. */ + +/* 예를 들어, 링크가 방문되지 않았을 때 */ +selected:link { } + +/* 또는 링크가 방문되었을 때 */ +selector:visited { } + +/* 또는 요소가 포커스에 있을 때 */ +selected:focus { } + +/* 또는 커서가 요소 위에 있을 때 */ +selector:hover { } + +/* 또는 링크를 클릭했을 때 */ +selector:active { } + +/* 링크에 관한 이러한 의사 클래스는 항상 위 순서대로 작성해야 합니다. 그렇지 않으면 코드가 예상대로 작동하지 않을 수 있습니다. */ + +/* 부모의 첫 번째 자식인 모든 요소 */ +selector:first-child {} + +/* 부모의 마지막 자식인 모든 요소 */ +selector:last-child {} + +/* 선택자 부모의 n번째 자식 선택 (CSS 3) */ +selector:nth-child(n) { } + +/* 의사 클래스와 마찬가지로 의사 요소는 문서의 특정 부분을 스타일링할 수 있도록 합니다. */ + +/* 선택한 요소의 가상 첫 번째 자식과 일치 */ +selector::before {} + +/* 선택한 요소의 가상 마지막 자식과 일치 */ +selector::after {} + +/* 적절한 위치에서 별표는 모든 요소를 선택하는 와일드카드로 사용될 수 있습니다. */ +* { } /* 모든 요소 */ +.parent * { } /* 모든 후손 */ +.parent > * { } /* 모든 자식 */ + +/* 그룹의 모든 선택자에 영향을 미치는 스타일을 정의하기 위해 여러 선택자를 그룹화합니다. */ +selector1, selector2 { } + +/* 특정 상태가 없는 요소를 선택합니다 (CSS 3) */ +/* 여기서는 id 속성이 없는 div를 선택합니다. */ +div:not([id]) { + background-color: red; +} + +/* #################### + ## 속성 + #################### */ + +selector { + + /* 길이 단위는 절대 또는 상대일 수 있습니다. */ + + /* 상대 단위 */ + width: 50%; /* 부모 요소 너비의 백분율 */ + font-size: 2em; /* 요소의 원래 글꼴 크기의 배수 */ + font-size: 2rem; /* 또는 루트 요소의 글꼴 크기 */ + font-size: 2vw; /* 뷰포트 너비의 1%의 배수 (CSS 3) */ + font-size: 2vh; /* 또는 높이 */ + font-size: 2vmin; /* vh 또는 vw 중 더 작은 것 */ + font-size: 2vmax; /* 또는 더 큰 것 */ + + /* 절대 단위 */ + width: 200px; /* 픽셀 */ + font-size: 20pt; /* 포인트 */ + width: 5cm; /* 센티미터 */ + min-width: 50mm; /* 밀리미터 */ + max-width: 5in; /* 인치 */ + + /* 색상 */ + color: #F6E; /* 짧은 16진수 형식 */ + color: #FF66EE; /* 긴 16진수 형식 */ + color: tomato; /* 명명된 색상 */ + color: rgb(255, 255, 255); /* rgb 값으로 */ + color: rgb(10%, 20%, 50%); /* rgb 백분율로 */ + color: rgba(255, 0, 0, 0.3); /* rgba 값으로 (CSS 3) 참고: 0 <= a <= 1 */ + color: transparent; /* 알파를 0으로 설정하는 것과 동일 */ + color: hsl(0, 100%, 50%); /* hsl 백분율로 (CSS 3) */ + color: hsla(0, 100%, 50%, 0.3); /* 알파가 있는 hsl 백분율로 */ + + /* 테두리 */ + border-width:5px; + border-style:solid; + border-color:red; /* background-color 설정과 유사 */ + border: 5px solid red; /* 동일한 약어 접근 방식 */ + border-radius:20px; /* CSS3 속성 */ + + /* 요소의 배경 이미지 */ + background-image: url(/img-path/img.jpg); /* url() 내부의 따옴표는 선택 사항 */ + + /* 글꼴 */ + font-family: Arial; + /* 글꼴 패밀리 이름에 공백이 있으면 따옴표로 묶어야 합니다. */ + font-family: "Courier New"; + /* 첫 번째 글꼴을 찾을 수 없으면 브라우저는 다음 글꼴을 사용합니다. */ + font-family: "Courier New", Trebuchet, Arial, sans-serif; +} + +/* 변수를 사용한 사용자 정의 CSS 속성 (CSS 3) */ +:root { + --main-bg-color: whitesmoke; +} +body { + background-color: var(--main-bg-color) +} + +/* 계산 수행 (CSS 3) */ +body { + width: calc(100vw - 100px) +} + +/* 다른 스타일 규칙 내부에 중첩 (CSS 3) */ +.main { + .bgred { /* .main .bgred { }와 동일 */ + background: red; + } + & .bggreen { /* .main .bggreen { }와 동일 */ + background: green; + } + &.bgblue { /* (공백 없음) .main.bgblue { }와 동일 */ + background: blue; + } +} + +/* flexbox를 사용한 반응형 레이아웃 디자인 (CSS 3) */ +.container { + display: flex; + flex-direction: row; /* flex 항목을 쌓는 방향 */ + flex-wrap: wrap; /* flex 항목이 줄 바꿈될지 여부 */ + justify-content: center; /* flex 항목을 가로로 정렬하는 방법 */ + align-items: center; /* flex 항목을 세로로 정렬하는 방법 */ +} +``` + +## 사용법 + +CSS 스타일시트를 `.css` 확장자로 저장하십시오. + +```html + + + + + + + +
+
+``` + +## 우선 순위 또는 캐스케이드 + +요소는 여러 선택자에 의해 대상으로 지정될 수 있으며, 한 번 이상 속성이 설정될 수 있습니다. 이러한 경우 규칙 중 하나가 다른 규칙보다 우선합니다. 더 구체적인 선택자를 가진 규칙이 덜 구체적인 선택자보다 우선하며, 스타일시트에 나중에 나타나는 규칙이 이전 규칙을 덮어씁니다(이는 두 개의 다른 연결된 스타일시트에 요소에 대한 규칙이 있고 규칙의 특이성이 동일한 경우 연결 순서가 우선하며 가장 나중에 연결된 시트가 스타일링을 제어한다는 의미이기도 합니다). + +이 프로세스를 캐스케이딩이라고 하며, 캐스케이딩 스타일 시트라는 이름이 붙었습니다. + +다음 CSS가 주어졌을 때: + +```css +/* A */ +p.class1[attr='value'] + +/* B */ +p.class1 { } + +/* C */ +p.class2 { } + +/* D */ +p { } + +/* E */ +p { property: value !important; } +``` + +그리고 다음 마크업: + +```html +

+``` + +스타일의 우선 순위는 다음과 같습니다. 각 **속성**에 대한 우선 순위이며 전체 블록에 대한 우선 순위가 아님을 기억하십시오. + +* `E`는 `!important` 키워드 때문에 가장 높은 우선 순위를 가집니다. 사용을 피하는 것이 좋습니다. +* `F`는 인라인 스타일이기 때문에 다음입니다. +* `A`는 다른 어떤 것보다 더 "구체적"이기 때문에 다음입니다. 3개의 지정자를 가집니다: 요소 이름 `p`, 클래스 `class1`, 속성 `attr='value'`. +* `C`는 `B`와 동일한 특이성을 가짐에도 불구하고 다음입니다. + `B` 다음에 나타나기 때문입니다. +* `B`는 다음입니다. +* `D`는 마지막입니다. + +## 미디어 쿼리 + +CSS 미디어 쿼리는 CSS 3의 기능으로, 특정 CSS 규칙을 적용해야 하는 시기(예: 인쇄 시 또는 특정 크기나 픽셀 밀도를 가진 화면에서)를 지정할 수 있습니다. 선택자의 특이성을 추가하지 않습니다. + +```css +/* 모든 장치에서 사용될 규칙 */ +h1 { + font-size: 2em; + color: white; + background-color: black; +} + +/* 프린터에서 h1이 잉크를 덜 사용하도록 변경 */ +@media print { + h1 { + color: black; + background-color: white; + } +} + +/* 너비가 480px 이상인 화면에 표시될 때 글꼴을 더 크게 만듭니다. */ +@media screen and (min-width: 480px) { + h1 { + font-size: 3em; + font-weight: normal; + } +} +``` + +미디어 쿼리에는 다음 기능이 포함될 수 있습니다: +`width`, `height`, `device-width`, `device-height`, `orientation`, `aspect-ratio`, `device-aspect-ratio`, `color`, `color-index`, `monochrome`, `resolution`, `scan`, `grid`. 이러한 기능 대부분은 `min-` 또는 `max-` 접두사를 가질 수 있습니다. + +`resolution` 기능은 이전 장치에서 지원되지 않으며, 대신 `device-pixel-ratio`를 사용하십시오. + +많은 스마트폰과 태블릿은 `viewport` 메타 태그를 제공하지 않으면 페이지를 데스크톱에서처럼 렌더링하려고 시도합니다. + +```html + + + +``` + +## 호환성 + +CSS2의 대부분의 기능 및 CSS3의 많은 기능은은모든 브라우저 및 장치에서 사용할 수 있습니다. 그러나 항상 새 기능을 사용하기 전에 확인하는 것이 좋습니다. + +## 자료 + +* [CanIUse](http://caniuse.com) (자세한 호환성 정보) +* [Dabblet](http://dabblet.com/) (CSS 플레이그라운드) +* [Mozilla 개발자 네트워크의 CSS 문서](https://developer.mozilla.org/en-US/docs/Web/CSS) (튜토리얼 및 참조) +* [Codrops의 CSS 참조](http://tympanus.net/codrops/css_reference/) (참조) +* [DevTips의 CSS 기본 사항](https://www.youtube.com/playlist?list=PLqGj3iMvMa4IOmy04kDxh_hqODMqoeeCy) (튜토리얼) + +## 더 읽을거리 + +* [CSS의 스타일 우선 순위 이해: 특이성, 상속 및 캐스케이드](http://www.vanseodesign.com/css/css-specificity-inheritance-cascaade/) +* [속성을 사용하여 요소 선택](https://css-tricks.com/almanac/selectors/a/attribute/) +* [QuirksMode CSS](http://www.quirksmode.org/css/) +* [Z-Index - 스택 컨텍스트](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context) +* [SASS](http://sass-lang.com/) 및 [LESS](http://lesscss.org/) CSS 전처리용 +* [CSS-Tricks](https://css-tricks.com) \ No newline at end of file diff --git a/ko/csv.md b/ko/csv.md new file mode 100644 index 0000000000..e20a5dcc1c --- /dev/null +++ b/ko/csv.md @@ -0,0 +1,77 @@ +--- +name: CSV +contributors: +- ["Timon Erhart", 'https://github.com/turbotimon/'] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +CSV(쉼표로 구분된 값)는 표 형식 데이터를 일반 텍스트로 저장하는 경량 파일 형식으로, 프로그램 간, 특히 스프레드시트와 데이터베이스 간의 쉬운 데이터 교환을 위해 설계되었습니다. 단순성과 인간이 읽을 수 있는 가독성 덕분에 데이터 상호 운용성의 초석이 되었습니다. +호환되지 않거나 독점적인 형식을 가진 프로그램 간에 데이터를 이동하는 데 자주 사용됩니다. + +RFC 4180은 형식에 대한 표준을 제공하지만, 실제로는 "CSV"라는 용어가 다음을 나타내는 모든 텍스트 파일을 더 광범위하게 지칭하는 데 사용됩니다. + +- 표 형식 데이터로 해석될 수 있습니다. +- 필드(열)를 구분하는 구분 기호를 사용합니다. +- 레코드(행)를 구분하는 줄 바꿈을 사용합니다. +- 선택적으로 첫 번째 행에 헤더를 포함합니다. + +```csv +Name, Age, DateOfBirth +Alice, 30, 1993-05-14 +Bob, 25, 1998-11-02 +Charlie, 35, 1988-03-21 +``` + +## 행 및 열 구분 기호 + +행은 일반적으로 줄 바꿈(`\n` 또는 `\r\n`)으로 구분되며, 열(필드)은 특정 구분 기호로 구분됩니다. 쉼표가 필드에 가장 일반적인 구분 기호이지만, 세미콜론(`;`)과 같은 다른 문자는 쉼표가 소수점 구분 기호로 사용되는 지역(예: 독일)에서 일반적으로 사용됩니다. 탭(`\t`)도 경우에 따라 구분 기호로 사용되며, 이러한 파일은 종종 "TSV"(탭으로 구분된 값)라고 불립니다. + +구분 기호로 세미콜론과 소수점 구분 기호로 쉼표를 사용하는 예: + +```csv +Name; Age; Grade +Alice; 30; 50,50 +Bob; 25; 45,75 +Charlie; 35; 60,00 +``` + +## 데이터 유형 + +CSV 파일은 본질적으로 데이터 유형을 정의하지 않습니다. 숫자와 날짜는 일반 텍스트로 저장되며, 해석은 파일을 가져오는 소프트웨어에 따라 달라집니다. 일반적으로 데이터는 다음과 같이 해석됩니다. + +```csv +Data, Comment +100, 정수(integer)로 해석됩니다. +100.00, 숫자(floating-point)로 해석됩니다. +2024-12-03, 날짜 또는 문자열로 해석됩니다(파서에 따라 다름). +Hello World, 텍스트(문자열)로 해석됩니다. +"1234", 숫자가 아닌 텍스트로 해석됩니다. +``` + +## 문자열 및 특수 문자 인용 + +문자열에 구분 기호, 특수 문자 또는 숫자로 해석될 수 있는 내용이 포함된 경우에만 문자열을 인용해야 합니다. 그러나 가독성과 견고성을 높이기 위해 모든 문자열을 인용하는 것이 좋은 관행으로 간주됩니다. + +```csv +문자열 인용 예제, +인용되지 않은 문자열, +"선택적으로 인용된 문자열(좋은 관행)", +"구분 기호가 포함된 경우 인용해야 합니다.", +"또한 \n 줄 바꿈 또는 \t 탭과 같은 특수 문자가 포함된 경우", +"인용 "" 문자 자체는 일반적으로 따옴표를 두 번 사용하여 이스케이프됩니다("")", +"또는 일부 시스템에서는 백슬래시 \" (다른 이스케이프와 같이)", +``` + +그러나 한 문서에 대해 인용 방법이 일관적인지 확인하십시오. 예를 들어, "" 또는 \"로 인용하는 마지막 두 예제는 일관적이지 않으며 문제를 일으킬 수 있습니다. + +## 인코딩 + +다양한 인코딩이 사용됩니다. 대부분의 최신 CSV 파일은 UTF-8 인코딩을 사용하지만, 이전 시스템은 ASCII 또는 ISO-8859와 같은 다른 인코딩을 사용할 수 있습니다. + +파일이 다른 시스템 간에 전송되거나 공유되는 경우, 문자 오해 문제를 피하기 위해 사용된 인코딩을 명시적으로 정의하는 것이 좋습니다. + +## 더 많은 자료 + ++ [위키백과](https://en.wikipedia.org/wiki/Comma-separated_values) ++ [RFC 4180](https://datatracker.ietf.org/doc/html/rfc4180) \ No newline at end of file diff --git a/ko/cue.md b/ko/cue.md new file mode 100644 index 0000000000..e84d186423 --- /dev/null +++ b/ko/cue.md @@ -0,0 +1,523 @@ +--- +name: CUE +filename: learncue.cue +contributors: + - ["Daniel Cox", "https://github.com/danielpcox"] + - ["Coleman McFarland", "https://github.com/dontlaugh"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +CUE는 표현력이 풍부한(하지만 튜링 완전하지는 않은) JSON 상위 집합으로, JSON 또는 YAML로 내보낼 수 있습니다. 선택적 유형과 대규모 구성 집합 작업에 대한 기타 여러 편의 기능을 지원합니다. 통합 엔진은 논리 프로그래밍에 뿌리를 두고 있으며, 따라서 현대 구성 관리 문제에 대한 즉각적인 솔루션을 제공합니다. + +CUE가 JSON으로 내보내질 때, 처리된 모든 파일의 값은 하나의 거대한 객체로 통합됩니다. 다음 두 파일을 고려하십시오: + +```yaml +//name.cue +name: "Daniel" +``` + +```yaml +//disposition.cue +disposition: "oblivious" +``` + +이제 JSON으로 통합하고 내보낼 수 있습니다: + +```bash +% cue export name.cue disposition.cue +{ + "name": "Daniel", + "disposition": "oblivious" +} +``` + +또는 YAML: + +```bash +% cue export --out yaml name.cue disposition.cue +name: Daniel +disposition: oblivious +``` + +C 스타일 주석은 출력에 포함되지 않습니다. 또한 CUE 구문의 키는 따옴표가 필요하지 않습니다. 일부 특수 문자는 따옴표가 필요합니다: + +```yaml +works_fine: true +"needs-quotes": true +``` + +통합은 파일 간에만 통합되는 것이 아니라 모든 유형과 값의 *전역 병합*입니다. 다음은 *유형*이 다르기 때문에 실패합니다. + +```yaml +//string_value.cue +foo: "baz" +``` + +```yaml +//integer_value.cue +foo: 100 +``` + +```bash +% cue export string_value.cue integer_value.cue +foo: "baz"와 100의 충돌 값(문자열과 int 유형 불일치): + integer_value.cue:1:6 + string_value.cue:1:6 +``` + +하지만 정수를 인용하더라도 *값*이 충돌하고 모든 것을 최상위 객체로 통합할 방법이 없기 때문에 여전히 실패합니다. + +```yaml +//string_value.cue +foo: "baz" +``` + +```yaml +//integer_value.cue +foo: "100" // 이제 문자열 +``` + +```bash +% cue export string_value.cue integer_value.cue +foo: "100"과 "baz"의 충돌 값: + integer_value.cue:1:6 + string_value.cue:1:6 +``` + +CUE의 유형은 값입니다. 통합 엔진이 특정 동작을 가진다고 아는 특수 값입니다. 통합 중에 값은 지정된 유형과 일치해야 하며, 구체적인 값이 필요한 경우 유형만 있으면 오류가 발생합니다. 따라서 다음은 괜찮습니다: + +```yaml +street: "1 Infinite Loop" +street: string +``` + +`cue export`는 YAML 또는 JSON을 생성하는 반면, `cue eval`은 CUE를 생성합니다. 이는 YAML 또는 JSON을 CUE로 변환하거나 CUE 자체에서 통합된 출력을 검사하는 데 유용합니다. CUE에서 구체적인 값이 누락되어도 괜찮습니다(둘 다 사용 가능하고 일치하는 경우 CUE를 내보낼 때 구체적인 값을 선호하지만). + +```yaml +//type-only.cue +amount: float +``` + +```bash +% cue eval type-only.cue +amount: float +``` + +그러나 내보내려면(또는 `eval`에 `-c`로 요구하도록 지시하려면) 구체적인 값이 *필요합니다*: + +```bash +% cue export type-only.cue +amount: 불완전한 값 float +``` + +유형과 통합되는 값을 제공하면 모든 것이 잘 작동합니다. + +```yaml +//concrete-value.cue +amount: 3.14 +``` + +```bash +% cue export type-only.cue concrete-value.cue +{ + "amount": 3.14 +} +``` + +구체적인 값을 유형과 통합하는 방법은 공통 구문을 공유하는 것보다 훨씬 강력하며, 예를 들어 JSON 스키마보다 훨씬 간결합니다. 이런 식으로 스키마, 기본값 및 데이터는 모두 CUE로 표현할 수 있습니다. + +기본값은 별표를 사용하여 유형과 함께 제공될 수 있습니다: + +```yaml +// default-port.cue +port: int | *8080 +``` + +```bash +% cue eval default-port.cue +port: 8080 +``` + +열거형 스타일 옵션(CUE의 "분리")은 `|` 구분 기호로 지정할 수 있습니다: + +```yaml +//severity-enum.cue +severity: "high" | "medium" | "low" +severity: "unknown" +``` + +```bash +% cue eval severity-enum.cue +severity: 빈 분리에서 3개의 오류: +severity: "high"와 "unknown"의 충돌 값: + ./severity-enum.cue:1:11 + ./severity-enum.cue:1:48 +severity: "low"와 "unknown"의 충돌 값: + ./severity-enum.cue:1:31 + ./severity-enum.cue:1:48 +severity: "medium"과 "unknown"의 충돌 값: + ./severity-enum.cue:1:20 + ./severity-enum.cue:1:48 +``` + +구조체 분리도 가능합니다(표시되지 않았지만 예상대로 작동합니다). + +CUE에는 "정의"가 있으며, 다른 언어의 변수 선언처럼 사용할 수 있습니다. 또한 구조체 유형을 정의하는 데에도 사용됩니다. 정의 유형의 구조체를 `&`를 사용하여 일부 구체적인 값에 적용할 수 있습니다. 또한 `[...#Whatever]`를 사용하여 "#Whatever 유형의 목록"이라고 말할 수 있습니다. + +```yaml +// definitions.cue + +#DashboardPort: 1337 + +configs: { + host: "localhost" + port: #DashboardPort +} + +#Address: { + street: string + city: string + zip?: int // ?는 zip을 선택 사항으로 만듭니다. +} + +some_address: #Address & { + street: "1 Rocket Rd" + city: "Hawthorne" +} + +more_addresses: [...#Address] & [ + {street: "1600 Amphitheatre Parkway", city: "Mountain View", zip: "94043"}, + {street: "1 Hacker Way", city: "Menlo Park"} +] +``` + +```bash +% cue export --out yaml definitions.cue +configs: + host: localhost + port: 1337 +some_address: + street: 1 Rocket Rd + city: Hawthorne +more_addresses: + - street: 1600 Amphitheatre Parkway + city: Mountain View + zip: "94043" + - street: 1 Hacker Way + city: Menlo Park +``` + +CUE는 단순한 JSON 위에 제공하는 모든 설탕으로 상당한 시간을 절약할 수 있습니다. 여기서는 세 줄로 중첩된 구조를 정의, "수정" 및 유효성 검사합니다: (엔진에 `string`이 제약 조건임을 알리기 위해 `string` 주위에 사용된 `[]` 구문을 주목하십시오. 이 경우 `string`은 문자열이 아닙니다.) + +```yaml +//paths.cue + +// 경로-값 쌍 +outer: middle1: inner: 3 +outer: middle2: inner: 7 + +// 컬렉션-제약 조건 쌍 +outer: [string]: inner: int +``` + +```bash +% cue export paths.cue +{ + "outer": { + "middle1": { + "inner": 3 + }, + "middle2": { + "inner": 7 + } + } +} +``` + +같은 맥락에서 CUE는 단일 인수의 함수와 유사한 "템플릿"을 지원합니다. 여기서 `Name`은 `container` 바로 아래의 각 문자열 키에 바인딩되는 반면, 그 아래의 구조체는 평가됩니다. + +```yaml +//templates.cue + +container: [Name=_]: { + name: Name + replicas: uint | *1 + command: string +} + +container: sidecar: command: "envoy" + +container: service: { + command: "fibonacci" + replicas: 2 +} +``` + +```bash +% cue eval templates.cue +container: { + sidecar: { + name: "sidecar" + replicas: 1 + command: "envoy" + } + service: { + name: "service" + command: "fibonacci" + replicas: 2 + } +} +``` + +그리고 그러한 참조에 대해 이야기하는 동안 CUE는 범위 지정 참조를 지원합니다. + +```yaml +//scopes-and-references.cue +v: "top-level v" +b: v // 참조 +a: { + b: v // 최상위 v와 일치 +} + +let V = v +a: { + v: "a's inner v" + c: v // 내부 v와 일치 + d: V // a.v에 의해 가려진 최상위 v와 일치 +} +av: a.v // a의 v와 일치 +``` + +```bash +% cue eval --out yaml scopes-and-references.cue +``` + +```yaml +v: top-level v +b: top-level v +a: + b: top-level v + v: a's inner v + c: a's inner v + d: top-level v +av: a's inner v +``` + +명확성을 위해 출력에서 키 순서를 변경했습니다. 순서는 실제로 중요하지 않으며, 주어진 수준의 중복 키는 *모두* 통합됩니다. + +필드를 `_`로 접두사로 붙여 숨길 수 있습니다(내보낸 필드에 `_` 접두사가 필요한 경우 필드를 인용하십시오). + +```yaml +//hiddens.cue +"_foo": 2 +_foo: 3 +foo: 4 +_#foo: 5 +#foo : 6 +``` + +```bash +% cue eval hiddens.cue +"_foo": 2 +foo: 4 +#foo: 6 + +% cue export hiddens.cue +{ + "_foo": 2, + "foo": 4 +} +``` + +정의와 관련하여 `eval`과 `export`의 차이점을 주목하십시오. CUE에서 정의를 숨기려면 `_`를 접두사로 붙일 수 있습니다. + +값 및 필드 보간: + +```yaml +//interpolation.cue + +#expense: 90 +#revenue: 100 +message: "Your profit was $\( #revenue - #expense)" + +cat: { + type: "Cuddly" + "is\(type)": true +} +``` + +```bash +% cue export interpolation.cue +{ + "message": "Your profit was $10", + "cat": { + "type": "Cuddly", + "isCuddly": true + } +} +``` + +연산자, 목록 이해, 조건문, 가져오기... + +```yaml +//getting-out-of-hand-now.cue +import "strings" // 나중에 다시 다루겠습니다. + +// 연산자는 좋습니다. +g: 5 / 3 // CUE는 수학을 할 수 있습니다. +h: 3 * "blah" // 그리고 파이썬과 같은 문자열 반복 +i: 3 * [1, 2, 3] // 목록도 마찬가지입니다. +j: 8 < 10 // 그리고 부울 연산도 지원합니다. + +// 조건문도 좋습니다. +price: number +// 가격이 너무 높으면 정당화가 필요합니다. +if price > 100 { + justification: string +} +price: 200 +justification: "impulse buy" + +// 목록 이해는 강력하고 간결합니다. +#items: [ 1, 2, 3, 4, 5, 6, 7, 8, 9] +comp: [ for x in #items if x rem 2 == 0 {x*x}] + +// 그리고... 음, 이것도 할 수 있습니다. +#a: [ "Apple", "Google", "SpaceX"] +for k, v in #a { + "\( strings.ToLower(v) )": { + pos: k + 1 + name: v + nameLen: len(v) + } +} +``` + +```bash +% cue export getting-out-of-hand-now.cue +``` + +```json +{ + "g": 1.66666666666666666666667, + "h": "blahblahblah", + "i": [1, 2, 3, 1, 2, 3, 1, 2, 3], + "j": true, + "apple": { + "pos": 1, + "name": "Apple", + "nameLen": 5 + }, + "google": { + "pos": 2, + "name": "Google", + "nameLen": 6 + }, + "price": 200, + "justification": "impulse buy", + "comp": [ + 4, + 16, + 36, + 64 + ], + "spacex": { + "pos": 3, + "name": "SpaceX", + "nameLen": 6 + } +} +``` + +이 시점에서 CUE는 튜링 완전하지 않을 수 있지만, 발등을 찍을 만큼 강력하므로 명확하게 유지하도록 노력해야 합니다. 조심하지 않으면 구성 작업을 *더 어렵게* 만들 수 있습니다. 적어도 주석을 활용하거나... + +이를 위해 CUE는 패키지와 모듈을 지원합니다. CUE 파일은 기본적으로 독립 실행형이지만, 상단에 패키지 절을 넣으면 해당 파일이 동일한 패키지 내의 다른 파일과 통합될 수 있음을 의미합니다. + +```yaml +//a.cue +package config + +foo: 100 +bar: int +``` + +```yaml +//b.cue +package config + +bar: 200 +``` + +새 디렉토리에 이 두 파일을 만들고 `cue eval`을 실행하면(인수 없음) 예상대로 통합됩니다. 현재 디렉토리에서 .cue 파일을 검색하고, 모두 동일한 패키지를 가지고 있으면 통합됩니다. + +패키지는 "모듈"의 맥락에서 더 명확합니다. 모듈은 조직의 *가장 큰* 단위입니다. 기본적으로 여러 파일에 걸쳐 있는 프로젝트가 있을 때마다 모듈을 만들고 URL의 도메인 및 경로와 유사한 이름(예: `example.com/something`)으로 이름을 지정해야 합니다. 이 모듈에서 무엇이든 가져올 때, 모듈 *내부*에서 가져오더라도 이 모듈 이름이 접두사로 붙는 완전한 모듈 경로를 사용해야 합니다. + +새 모듈을 다음과 같이 만들 수 있습니다: + +```bash +mkdir mymodule && cd mymodule +cue mod init example.com/mymodule +``` + +이렇게 하면 `mymodule` 디렉토리 내에 `cue.mod/` 하위 디렉토리가 생성되며, `cue.mod/`에는 다음 파일과 하위 디렉토리가 포함됩니다: + +- `module.cue` (이 경우 `module: "example.com/mymodule"`로 모듈 이름을 정의합니다) +- pkg/ +- gen/ +- usr/ + +이에 대한 다른 관점과 내용에 대한 자세한 내용은 [cuelang.org/docs/concepts/packages/](https://cuelang.org/docs/concepts/packages/)를 참조하십시오. 여기서는 이 디렉토리의 내용에 대해 *전혀* 생각할 필요가 없다고 말하겠습니다. 단, 모듈 이름은 모듈 내의 모든 가져오기에 대한 접두사가 됩니다. + +모듈 파일 계층 구조는 어디로 갈까요? 모듈의 모든 파일과 디렉토리는 `mymodule/`에 루트를 둡니다. 이 디렉토리에는 `cue.mod/`도 포함됩니다. 패키지를 가져오려면 `example.com/mymodule`을 접두사로 붙이고 `mymodule/`에 루트를 둔 상대 경로를 사용해야 합니다. + +구체적으로 설명하자면, 다음을 고려하십시오: + +``` +mymodule +├── config +│ ├── a.cue +│ └── b.cue +├── cue.mod +│ ├── module.cue +│ ├── pkg +│ └── usr +└── main.cue +``` + +`cue.mod/` 및 그 아래 파일은 `cue mod init example.com/mymodule`에 의해 생성되었습니다. 그런 다음 `a.cue` 및 `b.cue`가 포함된 `config/` 하위 디렉토리를 만들었습니다. 그런 다음 모든 것을 제어하는 최상위 파일 역할을 하는 `main.cue`를 만들었습니다. + +`eval`을 실행하면(인수 없음) 현재 디렉토리의 모든 .cue 파일에 패키지가 하나만 있는지 확인하고, 그렇다면 통합하여 결과를 출력합니다. 이 경우 main.cue에만 패키지 `main`이 있으므로(여기서 "main"에 특별한 것은 없지만 적절해 보였습니다) 그것이 하나입니다. + +`config/a.cue` 및 `config/b.cue`는 이전 파일이지만, 이제 둘 다 상단에 `package config`가 있습니다: + +```yaml +//a.cue +package config + +foo: 100 +bar: int +``` + +```yaml +//b.cue +package config + +bar: 200 +``` + +따라서 그렇습니다. `config/` 아래의 두 파일이 실제로 통합되는지 확인하려면 `a.cue`에서 `bar: int`를 `bar: string`으로 변경하고 `cue eval`을 다시 실행하여 멋진 유형 오류를 얻을 수 있습니다: + +``` +cue eval 2022-01-06 17:51:24 +configuredBar: "string"과 200의 충돌 값(문자열과 int 유형 불일치): + ./config/a.cue:4:6 + ./config/b.cue:3:6 + ./main.cue:5:16 +``` + +지금은 여기까지입니다. 앞으로 더 많은 패키지 관리 기능이 추가될 것이며 `cue.mod`에 대한 설계 결정은 이를 미리 내다보고 있다는 것을 이해합니다. + +마지막으로, CUE에는 강력한 기능을 가진 내장 모듈이 있습니다. 이전에 "strings"를 가져와 `strings.ToLower`를 사용했을 때 그 중 하나를 보았습니다. 완전한 모듈 이름이 없는 가져오기는 내장 기능으로 간주됩니다. 전체 목록과 각 기능에 대한 문서는 여기에서 찾을 수 있습니다: [pkg.go.dev/cuelang.org/go/pkg](https://pkg.go.dev/cuelang.org/go/pkg) + +이것은 공식 문서 및 튜토리얼을 요약한 것이므로 원본 자료에 관심을 가져주십시오: [cuelang.org/docs/tutorials/](https://cuelang.org/docs/tutorials/) \ No newline at end of file diff --git a/ko/cypher.md b/ko/cypher.md new file mode 100644 index 0000000000..569ed03023 --- /dev/null +++ b/ko/cypher.md @@ -0,0 +1,231 @@ +--- +name: Cypher +filename: LearnCypher.cql +contributors: + - ["Théo Gauchoux", "https://github.com/TheoGauchoux"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Cypher는 그래프를 쉽게 조작하기 위한 Neo4j의 쿼리 언어입니다. +SQL의 구문을 재사용하고 그래프를 나타내기 위해 ASCII 아트와 같은 것을 혼합합니다. +이 튜토리얼은 노드 및 관계와 같은 그래프 개념을 이미 알고 있다고 가정합니다. + +## 노드는 그래프의 레코드를 나타냅니다. + +`()`는 *노드*를 나타내는 빈 *노드*이지만, 쿼리와는 관련이 없습니다. + +`(n)`은 변수 `n`으로 참조되는 *노드*이며, 쿼리에서 재사용할 수 있습니다. 소문자로 시작하고 camelCase를 사용합니다. + +`(p:Person)` - 노드에 *레이블*을 추가할 수 있습니다. 여기서는 `Person`입니다. 유형/클래스/범주와 같습니다. 대문자로 시작하고 camelCase를 사용합니다. + +`(p:Person:Manager)` - 노드는 여러 *레이블*을 가질 수 있습니다. + +`(p:Person {name : 'Théo Gauchoux', age : 22})` - 노드는 일부 *속성*을 가질 수 있습니다. 여기서는 `name`과 `age`입니다. 소문자로 시작하고 camelCase를 사용합니다. + +속성에서 허용되는 유형: + +- 숫자 +- 부울 +- 문자열 +- 이전 기본 유형 목록 + +*경고: Cypher에는 날짜/시간 속성이 없습니다! 특정 패턴의 문자열 또는 특정 날짜의 숫자를 사용할 수 있습니다.* + +`p.name` - 점 스타일로 속성에 액세스할 수 있습니다. + +## 관계(또는 에지)는 두 노드를 연결합니다. + +`[:KNOWS]`는 *레이블* `KNOWS`가 있는 *관계*입니다. 노드의 레이블과 같은 *레이블*입니다. UPPER_SNAKE_CASE를 사용합니다. + +`[k:KNOWS]` - 변수 `k`로 참조되는 동일한 *관계*이지만, 쿼리에서 재사용할 필요는 없습니다. + +`[k:KNOWS {since:2017}]` - *속성*(`노드`와 유사)이 있는 동일한 *관계*입니다. 여기서는 `since`입니다. + +`[k:KNOWS*..4]`는 *경로*(나중에 설명)에서 사용할 구조 정보입니다. 여기서 `*..4`는 "`k` 관계가 1에서 4번 반복될 수 있는 패턴과 일치"를 의미합니다. + +## 경로 - 노드와 관계를 혼합하는 방법. + +`(a:Person)-[:KNOWS]-(b:Person)` - `a`와 `b`가 서로 아는 관계를 설명하는 경로입니다. + +`(a:Person)-[:MANAGES]->(b:Person)` - 경로는 방향을 가질 수 있습니다. 이 경로는 `a`가 `b`의 관리자임을 설명합니다. + +`(a:Person)-[:KNOWS]-(b:Person)-[:KNOWS]-(c:Person)` - 여러 관계를 연결할 수 있습니다. 이 경로는 친구의 친구를 설명합니다. + +`(a:Person)-[:MANAGES]->(b:Person)-[:MANAGES]->(c:Person)` - 체인도 방향을 가질 수 있습니다. 이 경로는 `a`가 `b`의 상사이고 `c`의 대리인임을 설명합니다. + +일반적으로 사용되는 패턴(Neo4j 문서에서): + +```cypher +// 친구의 친구 +(user)-[:KNOWS]-(friend)-[:KNOWS]-(foaf) + +// 최단 경로 +path = shortestPath( (user)-[:KNOWS*..5]-(other) ) + +// 협업 필터링 +(user)-[:PURCHASED]->(product)<-[:PURCHASED]-()-[:PURCHASED]->(otherProduct) + +// 트리 탐색 +(root)<-[:PARENT*]-(leaf:Category)-[:ITEM]->(data:Product) +``` + +## 쿼리 생성 + +새 노드 생성 + +```cypher +CREATE (a:Person {name:"Théo Gauchoux"}) +RETURN a +``` + +*`RETURN`은 쿼리 후 결과를 얻을 수 있도록 합니다. `RETURN a, b`와 같이 여러 개일 수 있습니다.* + +새 관계 생성 (2개의 새 노드 포함) + +```cypher +CREATE (a:Person)-[k:KNOWS]-(b:Person) +RETURN a,k,b +``` + +## 쿼리 일치 + +모든 노드 일치 + +```cypher +MATCH (n) +RETURN n +``` + +레이블별 노드 일치 + +```cypher +MATCH (a:Person) +RETURN a +``` + +레이블 및 속성별 노드 일치 + +```cypher +MATCH (a:Person {name:"Théo Gauchoux"}) +RETURN a +``` + +관계에 따라 노드 일치 (방향 없음) + +```cypher +MATCH (a)-[:KNOWS]-(b) +RETURN a,b +``` + +관계에 따라 노드 일치 (방향 있음) + +```cypher +MATCH (a)-[:MANAGES]->(b) +RETURN a,b +``` + +`WHERE` 절이 있는 노드 일치 + +```cypher +MATCH (p:Person {name:"Théo Gauchoux"})-[s:LIVES_IN]->(city:City) +WHERE s.since = 2015 +RETURN p,state +``` + +`MATCH WHERE` 절을 `CREATE` 절과 함께 사용할 수 있습니다. + +```cypher +MATCH (a), (b) +WHERE a.name = "Jacquie" AND b.name = "Michel" +CREATE (a)-[:KNOWS]-(b) +``` + +## 쿼리 업데이트 + +노드의 특정 속성 업데이트 + +```cypher +MATCH (p:Person) +WHERE p.name = "Théo Gauchoux" +SET p.age = 23 +``` + +노드의 모든 속성 교체 + +```cypher +MATCH (p:Person) +WHERE p.name = "Théo Gauchoux" +SET p = {name: "Michel", age: 23} +``` + +노드에 새 속성 추가 + +```cypher +MATCH (p:Person) +WHERE p.name = "Théo Gauchoux" +SET p += {studies: "IT Engineering"} +``` + +노드에 레이블 추가 + +```cypher +MATCH (p:Person) +WHERE p.name = "Théo Gauchoux" +SET p:Internship +``` + +## 쿼리 삭제 + +특정 노드 삭제 (연결된 관계는 먼저 삭제해야 함) + +```cypher +MATCH (p:Person)-[relationship]-() +WHERE p.name = "Théo Gauchoux" +DELETE relationship, p +``` + +특정 노드에서 속성 제거 + +```cypher +MATCH (p:Person) +WHERE p.name = "Théo Gauchoux" +REMOVE p.age +``` + +*`REMOVE` 키워드에 주의하십시오. `DELETE`가 아닙니다!* + +특정 노드에서 레이블 제거 + +```cypher +MATCH (p:Person) +WHERE p.name = "Théo Gauchoux" +DELETE p:Person +``` + +전체 데이터베이스 삭제 + +```cypher +MATCH (n) +OPTIONAL MATCH (n)-[r]-() +DELETE n, r +``` + +*정말, Cypher의 `rm -rf /`입니다!* + +## 기타 유용한 절 + +`PROFILE` - 쿼리 전에 실행 계획을 표시합니다. + +`COUNT(e)` - `e`와 일치하는 엔티티(노드 또는 관계) 수를 계산합니다. + +`LIMIT x` - 결과를 처음 `x`개로 제한합니다. + +## 특별 힌트 + +- Cypher는 이중 슬래시를 사용하는 한 줄 주석만 있습니다: `// comment` +- .cql 파일에 저장된 Cypher 스크립트를 Neo4j에서 직접 실행할 수 있습니다(가져오기입니다). 그러나 이 파일에는 여러 문(세미콜론으로 구분)을 가질 수 없습니다. +- Cypher를 작성하려면 Neo4j 셸을 사용하십시오. 정말 훌륭합니다. +- Cypher는 모든 그래프 데이터베이스의 표준 쿼리 언어가 될 것입니다([openCypher](https://opencypher.org/)로 알려짐). + +[여기](https://neo4j.com/developer/cypher-query-language/)에서 더 읽어보십시오. \ No newline at end of file diff --git a/ko/d.md b/ko/d.md new file mode 100644 index 0000000000..53e800d47e --- /dev/null +++ b/ko/d.md @@ -0,0 +1,231 @@ +--- +name: D +filename: learnd.d +contributors: + - ["Nick Papanastasiou", "www.nickpapanastasiou.github.io"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```d +// 무슨 일이 일어날지 아실 겁니다... +module hello; + +import std.stdio; + +// args는 선택 사항입니다. +void main(string[] args) { + writeln("Hello, World!"); +} +``` + +저처럼 인터넷에서 너무 많은 시간을 보낸다면 [D](http://dlang.org/)에 대해 들어봤을 가능성이 높습니다. D 프로그래밍 언어는 저수준 기능부터 표현력이 풍부한 고수준 추상화까지 모든 것을 지원하는 현대적이고 범용적인 다중 패러다임 언어입니다. + +D는 [Walter Bright](https://en.wikipedia.org/wiki/Walter_Bright)와 [Andrei Alexandrescu](https://en.wikipedia.org/wiki/Andrei_Alexandrescu)가 주도하는 대규모의 매우 똑똑한 그룹에 의해 활발하게 개발되고 있습니다. +이제 모든 것을 제쳐두고 몇 가지 예제를 살펴보겠습니다! + +```d +import std.stdio; + +void main() { + + // 조건문과 루프는 예상대로 작동합니다. + for(int i = 0; i < 10000; i++) { + writeln(i); + } + + // 'auto'는 유형 추론에 사용할 수 있습니다. + auto n = 1; + + // 숫자 리터럴은 가독성을 위해 '_'를 자릿수 구분 기호로 사용할 수 있습니다. + while(n < 10_000) { + n += n; + } + + do { + n -= (n / 2); + } while(n > 0); + + // For와 while은 좋지만, D 언어에서는 'foreach' 루프를 선호합니다. + // '..'는 첫 번째 값을 포함하고 마지막 값을 제외하는 연속적인 범위를 만듭니다. + foreach(n; 1..1_000_000) { + if(n % 2 == 0) + writeln(n); + } + + // 역방향으로 반복하고 싶을 때는 'foreach_reverse'도 있습니다. + foreach_reverse(n; 1..int.max) { + if(n % 2 == 1) { + writeln(n); + } else { + writeln("No!"); + } + } +} +``` + +`struct`, `class`, `union`, `enum`으로 새 유형을 정의할 수 있습니다. 구조체와 공용체는 값으로 함수에 전달되고(즉, 복사됨) 클래스는 참조로 전달됩니다. 또한 템플릿을 사용하여 유형과 값 모두에 대해 이 모든 것을 매개변수화할 수 있습니다! + +```d +// 여기서 'T'는 유형 매개변수입니다. C++/C#/Java의 ''를 생각하십시오. +struct LinkedList(T) { + T data = null; + + // 매개변수화된 유형을 인스턴스화하려면 '!'를 사용하십시오. 다시 말하지만, ''를 생각하십시오. + LinkedList!(T)* next; +} + +class BinTree(T) { + T data = null; + + // 템플릿 매개변수가 하나만 있는 경우 괄호를 생략할 수 있습니다. + BinTree!T left; + BinTree!T right; +} + +enum Day { + Sunday, + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, +} + +// 유형에 대한 약어를 만들려면 alias를 사용하십시오. +alias IntList = LinkedList!int; +alias NumTree = BinTree!double; + +// 함수 템플릿도 만들 수 있습니다! +T max(T)(T a, T b) { + if(a < b) + return b; + + return a; +} + +// 참조로 전달을 보장하려면 ref 키워드를 사용하십시오. 즉, 'a'와 'b'가 값 유형이더라도 +// 'swap()'에는 항상 참조로 전달됩니다. +void swap(T)(ref T a, ref T b) { + auto temp = a; + + a = b; + b = temp; +} + +// 템플릿을 사용하면 유형뿐만 아니라 값으로도 매개변수화할 수 있습니다. +class Matrix(uint m, uint n, T = int) { + T[m] rows; + T[n] columns; +} + +auto mat = new Matrix!(3, 3); // 유형 'T'는 'int'로 기본 설정되었습니다. +``` + +클래스에 대해 말하자면, 속성에 대해 잠시 이야기해 봅시다. 속성은 lvalue처럼 작동할 수 있는 함수이므로 POD 구조체(`structure.x = 7`)의 구문을 getter 및 setter 메서드(`object.setX(7)`)의 의미와 함께 사용할 수 있습니다! + +```d +// 유형 'T' 및 'U'로 매개변수화된 클래스를 고려하십시오. +class MyClass(T, U) { + T _data; + U _other; +} + +// 그리고 다음과 같은 "getter" 및 "setter" 메서드: +class MyClass(T, U) { + T _data; + U _other; + + // 생성자는 항상 'this'로 명명됩니다. + this(T t, U u) { + // 아래의 setter 메서드를 호출합니다. + data = t; + other = u; + } + + // getter + @property T data() { + return _data; + } + + @property U other() { + return _other; + } + + // setter + @property void data(T t) { + _data = t; + } + + @property void other(U u) { + _other = u; + } +} + +// 그리고 다음과 같이 사용합니다: +void main() { + auto mc = new MyClass!(int, string)(7, "seven"); + + // 콘솔에 쓰기 위해 표준 라이브러리에서 'stdio' 모듈을 가져옵니다. + // (가져오기는 범위에 로컬일 수 있습니다). + import std.stdio; + + // 값을 가져오기 위해 getter를 호출합니다. + writefln("Earlier: data = %d, str = %s", mc.data, mc.other); + + // 새 값을 할당하기 위해 setter를 호출합니다. + mc.data = 8; + mc.other = "eight"; + + // 새 값을 가져오기 위해 getter를 다시 호출합니다. + writefln("Later: data = %d, str = %s", mc.data, mc.other); +} +``` + +속성을 사용하면 getter 및 setter 메서드에 원하는 만큼의 논리를 추가하고 멤버에 직접 액세스하는 깔끔한 구문을 유지할 수 있습니다! + +사용 가능한 다른 객체 지향 기능으로는 인터페이스, 추상 클래스 및 메서드 재정의가 있습니다. D는 Java와 동일하게 상속을 지원합니다: 하나의 클래스를 확장하고 원하는 만큼의 인터페이스를 구현합니다. + +D의 OOP 기능을 살펴보았지만, 이제 주제를 바꿔 봅시다. D는 일급 함수, `pure` 함수 및 불변 데이터를 사용하여 함수형 프로그래밍을 제공합니다. 또한 좋아하는 모든 함수형 알고리즘(map, filter, reduce 등)은 훌륭한 `std.algorithm` 모듈에서 찾을 수 있습니다! + +```d +import std.algorithm : map, filter, reduce; +import std.range : iota; // 끝을 제외하는 범위 구축 +import std.stdio; + +void main() { + // 1부터 100까지의 짝수 정수의 제곱 목록의 합을 인쇄하고 싶습니다. 쉽습니다! + + // 람다 표현식을 템플릿 매개변수로 전달하기만 하면 됩니다! + // 원하는 함수를 전달할 수 있지만, 여기서는 람다가 편리합니다. + auto num = iota(1, 101).filter!(x => x % 2 == 0) + .map!(y => y ^^ 2) + .reduce!((a, b) => a + b); + + writeln(num); +} +``` + +num을 계산하기 위해 멋진 Haskellian 파이프라인을 구축한 방법을 주목하십시오. 이는 UFCS(Uniform Function Call Syntax)로 알려진 D 혁신 덕분입니다. UFCS를 사용하면 함수 호출을 메서드 또는 자유 함수 호출로 작성할지 선택할 수 있습니다! Walter는 이에 대한 좋은 기사를 [여기](http://www.drdobbs.com/cpp/uniform-function-call-syntax/232700394)에 작성했습니다. +간단히 말해, 첫 번째 매개변수가 특정 유형 A인 함수를 유형 A의 모든 표현식에서 메서드로 호출할 수 있습니다. + +병렬 처리를 좋아합니다. 다른 사람들도 병렬 처리를 좋아합니까? 물론이죠. 병렬 처리를 해 봅시다! + +```d +// 1부터 (배열 크기까지) 모든 연속 정수의 제곱근으로 큰 배열을 채우고 싶다고 가정해 봅시다. 그리고 사용 가능한 코어 수만큼 동시에 이 작업을 수행하고 싶습니다. + +import std.stdio; +import std.parallelism : parallel; +import std.math : sqrt; + +void main() { + // 큰 배열을 만듭니다. + auto arr = new double[1_000_000]; + + // 인덱스를 사용하고, 모든 배열 요소를 참조로 액세스하고(각 요소를 변경할 것이기 때문에) 배열에서 병렬로 호출합니다! + foreach(i, ref elem; parallel(arr)) { + elem = sqrt(i + 1.0); + } +} +``` \ No newline at end of file diff --git a/ko/dart.md b/ko/dart.md new file mode 100644 index 0000000000..43e2533603 --- /dev/null +++ b/ko/dart.md @@ -0,0 +1,709 @@ +--- +name: Dart +filename: learndart.dart +contributors: + - ["Joao Pedrosa", "https://github.com/jpedrosa/"] + - ["Vince Ramces Oliveros", "https://github.com/ram231"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +**Dart**는 단일 스레드, 범용 프로그래밍 언어입니다. +다른 주류 언어에서 많은 것을 차용했습니다. +스트림, 퓨처(JavaScript에서는 Promise로 알려짐), 제네릭, 일급 함수(클로저) 및 정적 타입 검사를 지원합니다. +Dart는 웹, CLI, 데스크톱, 모바일 및 IoT 장치와 같은 모든 플랫폼에서 실행될 수 있습니다. + +Dart의 가장 논란이 많은 기능은 ~~선택적 타이핑~~ 정적 타입 안전성 및 [사운드 타입 검사](https://dart.dev/guides/language/sound-dart)입니다. + +```dart +import "dart:collection"; +import "dart:math" as math; + +/// Y분 만에 Dart 배우기에 오신 것을 환영합니다. http://dart.dev/ +/// 이것은 실행 가능한 튜토리얼입니다. Dart로 실행하거나 +/// DartPad 사이트에 복사/붙여넣기하여 실행할 수 있습니다. http://dartpad.dev/ +/// 또한 < > New Pad를 클릭하고 Flutter를 선택하여 DartPad에서 Flutter를 실행할 수 있습니다. + + +/// Dart에서는 모든 것이 객체입니다. +/// 모든 객체 선언은 Null의 인스턴스이며 +/// Null도 객체입니다. + + +/// Dart의 3가지 주석 유형 +// 한 줄 주석 +/** +* 여러 줄 주석 +* 여러 줄을 주석 처리할 수 있습니다. +*/ +/// 코드 문서 주석 +/// API를 만들 때 코드 문서를 생성하기 위해 마크다운 구문을 사용합니다. +/// 코드 문서 주석은 API, 클래스 및 메서드를 문서화할 때 권장되는 선택입니다. + +/// 4가지 변수 선언 유형. +/// 상수는 변경할 수 없는 변수입니다. +/// Dart에서 `const`는 SCREAMING_SNAKE_CASE 이름 선언을 사용해야 합니다. +const CONSTANT_VALUE = "I CANNOT CHANGE"; +CONSTANT_VALUE = "DID I?"; // 오류 +/// Final은 한 번 인스턴스화되면 변경할 수 없는 또 다른 변수 선언입니다. 클래스 및 함수에서 일반적으로 사용됩니다. +/// `final`은 pascalCase로 선언할 수 있습니다. +final finalValue = "value cannot be changed once instantiated"; +finalValue = "Seems not"; // 오류 + +/// `var`는 변경 가능하며 값을 변경할 수 있는 또 다른 변수 선언입니다. Dart는 유형을 추론하며 데이터 유형을 변경하지 않습니다. +var mutableValue = "Variable string"; +mutableValue = "this is valid"; +mutableValue = false; // 오류. + +/// `dynamic`은 Dart 정적 타입 검사에서 유형이 평가되지 않는 또 다른 변수 선언입니다. +/// 값과 데이터 유형을 변경할 수 있습니다. +/// 일부 Dart 개발자는 데이터 유형을 추적할 수 없으므로 dynamic을 신중하게 사용합니다. 따라서 자신의 위험을 감수하고 사용하십시오. +dynamic dynamicValue = "I'm a string"; +dynamicValue = false; // false + + +/// 함수는 전역 공간에 선언될 수 있습니다. +/// 함수 선언과 메서드 선언은 동일하게 보입니다. 함수 +/// 선언은 name() {} 또는 name() => singleLineExpression; 형식입니다. +/// 뚱뚱한 화살표 함수 선언은 표현식 결과에 대한 암시적 또는 +/// 명시적 반환일 수 있습니다. +/// Dart는 Dart 프로젝트의 어디에서든 `main()`이라는 함수를 실행합니다. +/// +example1() { + nested1() { + nested2() => print("Example1 nested 1 nested 2"); + nested2(); + } + + nested1(); +} + +/// 익명 함수에는 이름이 없습니다. +example2() { + //// 명시적 반환 유형. + nested1(void Function() fn) { + fn(); + } + nested1(() => print("Example2 nested 1")); +} + +/// 함수 매개변수가 선언될 때, 선언은 매개변수가 취하는 이름을 명시적으로 지정하여 +/// 함수가 취하는 매개변수 수를 포함할 수 있습니다. +example3() { + planA(fn(String informSomething)) { + fn("Example3 plan A"); + } + planB(fn) { + // 또는 매개변수 수를 선언하지 않습니다. + fn("Example3 plan B"); + } + + planA((s) => print(s)); + planB((s) => print(s)); +} + +/// 함수는 외부 변수에 대한 클로저 액세스를 가집니다. +/// Dart는 변수에 값이 있는 경우 유형을 추론합니다. +/// 이 예제에서 Dart는 이 변수가 문자열임을 알고 있습니다. +var example4Something = "Example4 nested 1"; +example4() { + nested1(fn(informSomething)) { + fn(example4Something); + } + + nested1((s) => print(s)); +} + +/// sayIt 메서드가 있는 클래스 선언, 이전과 마찬가지로 함수처럼 외부 변수에 대한 클로저 액세스도 가집니다. +var example5method = "Example5 sayIt"; + +class Example5Class { + sayIt() { + print(example5method); + } +} + +example5() { + /// Example5Class의 익명 인스턴스를 생성하고 sayIt + /// 메서드를 호출합니다. + /// Dart에서 `new` 키워드는 선택 사항입니다. + new Example5Class().sayIt(); +} + +/// 클래스 선언은 class name { [classBody] } 형식입니다. +/// 여기서 classBody는 인스턴스 메서드 및 변수뿐만 아니라 +/// 클래스 메서드 및 변수도 포함할 수 있습니다. +class Example6Class { + var instanceVariable = "Example6 instance variable"; + sayIt() { + print(instanceVariable); + } +} + +example6() { + Example6Class().sayIt(); +} + +/// 클래스 메서드 및 변수는 "static" 용어로 선언됩니다. +class Example7Class { + static var classVariable = "Example7 class variable"; + static sayItFromClass() { + print(classVariable); + } + + sayItFromInstance() { + print(classVariable); + } +} + +example7() { + Example7Class.sayItFromClass(); + new Example7Class().sayItFromInstance(); +} + +/// Dart는 제네릭을 지원합니다. +/// 제네릭은 클래스가 작동하는 데이터 유형을 지정하지 않고 +/// 클래스에 대한 코드를 작성하는 기술을 나타냅니다. +/// 출처: https://stackoverflow.com/questions/4560890/what-are-generics-in-c + +/// 유형 `T`는 인스턴스화된 모든 유형을 나타냅니다. +/// 원하는 대로 호출할 수 있습니다. +/// 프로그래머는 다음 규칙을 사용합니다. +/// T - 유형(클래스 및 기본 유형에 사용) +/// E - 요소(목록, 집합 또는 반복 가능에 사용) +/// K,V - 키 값(맵에 사용) +class GenericExample{ + void printType(){ + print("$T"); + } + // 메서드도 제네릭을 가질 수 있습니다. + genericMethod(){ + print("class:$T, method: $M"); + } +} + + +/// 목록은 배열과 유사하지만 목록은 Iterable의 자식입니다. +/// 따라서 맵, 목록, 연결 목록은 모두 `for` 키워드를 사용하여 반복할 수 있도록 Iterable의 자식입니다. +/// 기억해야 할 중요한 사항: +/// () - Iterable +/// [] - List +/// {} - Map + + +/// 목록은 훌륭하지만, 함수/메서드 본문 외부에서 목록이 될 수 있는 것에 대한 제한이 있습니다. +/// 클래스 외부 또는 클래스 외부의 목록은 상수여야 합니다. 문자열과 숫자는 기본적으로 상수입니다. +/// 그러나 배열과 맵은 그렇지 않습니다. "const"로 선언하여 상수로 만들 수 있습니다. +/// JavaScript의 Object.freeze()와 다소 유사합니다. +const example8List = ["Example8 const array"]; +const example8Map = {"someKey": "Example8 const map"}; +/// 목록 또는 맵을 객체로 선언합니다. + List explicitList = new List.empty(); + Map explicitMaps = new Map(); + +example8() { + explicitList.add("SomeArray"); + print(example8Map["someKey"]); + print(explicitList[0]); + + /// 한 변수에서 다른 변수로 목록을 할당하면 동일한 결과가 나오지 않습니다. + /// Dart는 참조에 의한 값 전달이기 때문입니다. + /// 따라서 기존 목록을 새 변수에 할당하면 + /// 목록 대신 반복 가능 객체가 됩니다. + var iterableExplicitList = explicitList; + print(iterableExplicitList); // ("SomeArray"); "[]"는 "()"가 됩니다. + var newExplicitLists = explicitList.toList(); // Iterable를 List로 변환합니다. +} + +/// Dart의 루프는 표준 for () {} 또는 while () {} 루프, 약간 더 현대적인 for (.. in ..) {} 또는 forEach, map 및 where로 시작하는 많은 지원 기능을 가진 함수형 콜백 형식입니다. +var example9Array = const ["a", "b"]; +example9() { + for (int i = 0; i < example9Array.length; i++) { + print("Example9 for loop '${example9Array[i]}'"); + } + var i = 0; + while (i < example9Array.length) { + print("Example9 while loop '${example9Array[i]}'"); + i++; + } + for (final e in example9Array) { + print("Example9 for-in loop '${e}'"); + } + + example9Array.forEach((e) => print("Example9 forEach loop '${e}'")); + +} + +/// 문자열의 문자를 반복하거나 부분 문자열을 추출합니다. +var example10String = "ab"; +example10() { + for (var i = 0; i < example10String.length; i++) { + print("Example10 String character loop '${example10String[i]}'"); + } + for (var i = 0; i < example10String.length; i++) { + print("Example10 substring loop '${example10String.substring(i, i + 1)}'"); + } +} + +/// `int`, `double` 및 `num`은 지원되는 세 가지 숫자 형식입니다. +/// `num`은 `int` 또는 `double`일 수 있습니다. +/// `int` 및 `double`은 `num` 유형의 자식입니다. +example11() { + var i = 1 + 320, d = 3.2 + 0.01; + final num myFinalNumDouble = 2.2; + final num myFinalNumInt = 2; + final int myFinalInt = 1; + final double myFinalDouble = 0.1; + num myNumDouble = 2.2; + num myNumInt = 2; + int myInt = 1; + double myDouble = 0; // Dart는 소수점 접두사를 추가하여 0.0이 됩니다. + myNumDouble = myFinalInt; // 유효 + myNumDouble = myFinalDouble; // 유효 + myNumDouble = myFinalNumInt; // 유효 + + myInt = myNumDouble; // 오류 + myInt = myFinalDouble; // 오류 + myInt = myFinalNumInt; // 오류 (Dart 2.9에서 암시적 다운캐스트 제거됨) + myInt = myFinalNumInt as int; // 유효 + + myDouble = myFinalInt; // 오류 + myDouble = myFinalNumInt; // 오류 + myDouble = myFinalNumDouble; // 오류 (Dart 2.9에서 암시적 다운캐스트 제거됨) + myDouble = myFinalNumDouble as double; // 유효 + + print("Example11 int ${i}"); + print("Example11 double ${d}"); + +} + +/// DateTime은 날짜/시간 산술을 제공합니다. +example12() { + var now = new DateTime.now(); + print("Example12 now '${now}'"); + now = now.add(new Duration(days: 1)); + print("Example12 tomorrow '${now}'"); +} + +/// 정규 표현식이 지원됩니다. +example13() { + var s1 = "some string", s2 = "some", re = new RegExp("^s.+?g"); + match(s) { + if (re.hasMatch(s)) { + print("Example13 regexp matches '${s}'"); + } else { + print("Example13 regexp doesn't match '${s}'"); + } + } + + match(s1); + match(s2); +} + +/// 부울 표현식은 암시적 변환 및 동적 유형을 지원합니다. +example14() { + var a = true; + if (a) { + print("true, a is $a"); + } + a = false; + if (a) { + print("true, a is $a"); + } else { + print("false, a is $a"); /// 여기서 실행됩니다. + } + + /// 동적 유형의 null은 부울로 변환할 수 없습니다. + var b; /// b는 동적 유형입니다. + b = "abc"; + try { + if (b) { + print("true, b is $b"); + } else { + print("false, b is $b"); + } + } catch (e) { + print("error, b is $b"); /// 이것은 실행될 수 있지만 오류가 발생했습니다. + } + b = null; + if (b) { /// 어설션 실패: 부울 표현식은 null이 아니어야 합니다. + print("true, b is $b"); + } else { + print("false, b is $b"); + } + + /// 정적으로 유형이 지정된 null은 부울로 변환할 수 없습니다. + var c = "abc"; + c = null; + /// 컴파일 실패 + /// if (c) { + /// print("true, c is $c"); + /// } else { + /// print("false, c is $c"); + /// } +} + +/// try/catch/finally 및 throw는 예외 처리에 사용됩니다. +/// throw는 모든 객체를 매개변수로 받습니다. +example15() { + try { + try { + throw "Some unexpected error."; + } catch (e) { + print("Example15 an exception: '${e}'"); + throw e; /// 다시 던지기 + } + } catch (e) { + print("Example15 catch exception being re-thrown: '${e}'"); + } finally { + print("Example15 Still run finally"); + } +} + +/// 동적으로 긴 문자열을 만들 때 효율적으로 하려면 +/// StringBuffer를 사용하십시오. 또는 문자열 배열을 결합할 수 있습니다. +example16() { + var sb = new StringBuffer(), a = ["a", "b", "c", "d"], e; + for (e in a) { + sb.write(e); + } + print("Example16 dynamic string created with " + "StringBuffer '${sb.toString()}'"); + print("Example16 join string array '${a.join()}'"); +} + +/// 문자열은 추가 연산자 없이 문자열 목록을 나란히 배치하여 연결할 수 있습니다. + +example17() { + print("Example17 " + "concatenate " + "strings " + "just like that"); +} + +/// 문자열은 단일 따옴표 또는 이중 따옴표를 구분 기호로 사용하며 둘 사이에 실제 차이는 없습니다. +/// 주어진 유연성은 사용되는 구분 기호와 일치하는 콘텐츠를 이스케이프할 필요성을 피하는 데 유용할 수 있습니다. +/// 예를 들어, 문자열에 HTML 콘텐츠가 포함된 경우 HTML 속성의 이중 따옴표입니다. +example18() { + print('Example18 +Don\'t can\'t I\'m Etc +'); +} + +/// 삼중 단일 따옴표 또는 삼중 이중 따옴표가 있는 문자열은 +/// 여러 줄에 걸쳐 있으며 줄 구분 기호를 포함합니다. +example19() { + print('''Example19 +Example19 Don't can't I'm Etc +Example19 '''); +} + +/// 문자열에는 $ 문자를 사용한 멋진 보간 기능이 있습니다. +/// $ { [표현식] }을 사용하면 표현식의 반환 값이 보간됩니다. +/// 변수 이름 뒤에 $를 붙이면 해당 변수의 내용이 보간됩니다. +/// $는 문자열에 추가하기 위해 \$와 같이 이스케이프할 수 있습니다. +example20() { + var s1 = "'\${s}'", s2 = "'\$s'"; + print("Example20 $ interpolation ${s1} or $s2 works."); +} + +/// 선택적 유형은 API에 주석을 달 수 있도록 하며 +/// IDE가 더 잘 리팩토링하고 자동 완성하며 +/// 오류를 확인할 수 있도록 도와줍니다. 지금까지는 유형을 선언하지 않았으며 +/// 프로그램은 잘 작동했습니다. 사실, 유형은 런타임에 무시됩니다. +/// 유형이 잘못되어도 프로그램은 유형이 중요하지 않은 것처럼 +/// 실행됩니다. 유형 오류를 확인하는 런타임 매개변수가 있습니다. +/// 이는 개발 중에 유용하다고 알려진 검사 모드이지만, +/// 추가 검사로 인해 느리므로 배포 런타임에는 피합니다. +class Example21 { + List _names = []; + Example21() { + _names = ["a", "b"]; + } + List get names => _names; + set names(List list) { + _names = list; + } + + int get length => _names.length; + void add(String name) { + _names.add(name); + } +} + +void example21() { + Example21 o = new Example21(); + o.add("c"); + print("Example21 names '${o.names}' and length '${o.length}'"); + o.names = ["d", "e"]; + print("Example21 names '${o.names}' and length '${o.length}'"); +} + +/// 클래스 상속은 class name extends AnotherClassName {} 형식입니다. +class Example22A { + var _name = "Some Name!"; + get name => _name; +} + +class Example22B extends Example22A {} + +example22() { + var o = new Example22B(); + print("Example22 class inheritance '${o.name}'"); +} + +/// 클래스 믹스인은 class name extends SomeClass with AnotherClassName {} 형식으로도 사용할 수 있습니다. +/// 다른 클래스를 믹스인하려면 일부 클래스를 확장해야 합니다. +/// 믹스인의 템플릿 클래스는 현재 생성자를 가질 수 없습니다. +/// 믹스인은 주로 멀리 떨어진 클래스와 메서드를 공유하는 데 사용되므로 +/// 단일 상속이 재사용 가능한 코드에 방해가 되지 않습니다. +/// 믹스인은 클래스 선언 중 "with" 문을 따릅니다. +class Example23A {} + +/// Dart 3부터는 'mixin' 키워드가 필요합니다. +mixin Example23Utils { + addTwo(n1, n2) { + return n1 + n2; + } +} + +class Example23B extends Example23A with Example23Utils { + addThree(n1, n2, n3) { + return addTwo(n1, n2) + n3; + } +} + +example23() { + var o = new Example23B(), r1 = o.addThree(1, 2, 3), r2 = o.addTwo(1, 2); + print("Example23 addThree(1, 2, 3) results in '${r1}'"); + print("Example23 addTwo(1, 2) results in '${r2}'"); +} + +/// 클래스 생성자 메서드는 클래스와 동일한 이름을 사용하며 +/// SomeClass() : super() {} 형식입니다. 여기서 ": super()" 부분은 선택 사항이며 +/// 상수 매개변수를 상위 생성자에 위임하는 데 사용됩니다. +class Example24A { + var _value; + Example24A({value = "someValue"}) { + _value = value; + } + get value => _value; +} + +class Example24B extends Example24A { + Example24B({value = "someOtherValue"}) : super(value: value); +} + +example24() { + var o1 = new Example24B(), o2 = new Example24B(value: "evenMore"); + print("Example24 calling super during constructor '${o1.value}'"); + print("Example24 calling super during constructor '${o2.value}'"); +} + +/// 더 간단한 클래스의 경우 생성자 매개변수를 설정하는 바로 가기가 있습니다. +/// this.parameterName 접두사를 사용하기만 하면 매개변수를 +/// 동일한 이름의 인스턴스 변수에 설정합니다. +class Example25 { + var value, anotherValue; + Example25({this.value, this.anotherValue}); +} + +example25() { + var o = new Example25(value: "a", anotherValue: "b"); + print("Example25 shortcut for constructor '${o.value}' and " + "'${o.anotherValue}'"); +} + +/// 명명된 매개변수는 {} 사이에 선언될 때 사용할 수 있습니다. +/// 매개변수 순서는 {} 사이에 선언될 때 선택 사항일 수 있습니다. +/// 매개변수는 [] 사이에 선언될 때 선택 사항일 수 있습니다. +example26() { + var _name, _surname, _email; + setConfig1({name, surname}) { + _name = name; + _surname = surname; + } + + setConfig2(name, [surname, email]) { + _name = name; + _surname = surname; + _email = email; + } + + setConfig1(surname: "Doe", name: "John"); + print("Example26 name '${_name}', surname '${_surname}', " + "email '${_email}'"); + setConfig2("Mary", "Jane"); + print("Example26 name '${_name}', surname '${_surname}', " + "email '${_email}'"); +} + +/// final로 선언된 변수는 한 번만 설정할 수 있습니다. +/// 클래스의 경우 final 인스턴스 변수는 상수 +/// 생성자 매개변수를 통해 설정할 수 있습니다. +class Example27 { + final color1, color2; + /// final 인스턴스 변수를 설정하기 위한 약간의 유연성 + /// 콜론 뒤에 오는 구문: + Example27({this.color1, color2}) : color2 = color2; +} + +example27() { + final color = "orange", o = new Example27(color1: "lilac", color2: "white"); + print("Example27 color is '${color}'"); + print("Example27 color is '${o.color1}' and '${o.color2}'"); +} + +/// 라이브러리를 가져오려면 import "libraryPath"를 사용하거나 핵심 라이브러리인 경우 +/// import "dart:libraryName"을 사용하십시오. "pub" 패키지 관리도 있습니다. +/// import "package:packageName" 규칙을 따릅니다. +/// 상단에 import "dart:collection";를 참조하십시오. import는 다른 코드 선언보다 먼저 와야 합니다. +/// IterableBase는 dart:collection에서 가져옵니다. +class Example28 extends IterableBase { + var names; + Example28() { + names = ["a", "b"]; + } + get iterator => names.iterator; +} + +example28() { + var o = new Example28(); + o.forEach((name) => print("Example28 '${name}'")); +} + +/// 제어 흐름의 경우 다음이 있습니다: +/// * break 문이 있는 표준 switch +/// * if-else if-else 및 삼항 ..?..:.. 연산자 +/// * 클로저 및 익명 함수 +/// * break, continue 및 return 문 +example29() { + var v = true ? 30 : 60; + switch (v) { + case 30: + print("Example29 switch statement"); + break; + } + if (v < 30) { + } else if (v > 30) { + } else { + print("Example29 if-else statement"); + } + callItForMe(fn()) { + return fn(); + } + + rand() { + v = new math.Random().nextInt(50); + return v; + } + + while (true) { + print("Example29 callItForMe(rand) '${callItForMe(rand)}'"); + if (v != 30) { + break; + } else { + continue; + } + /// 여기에 도달하지 않습니다. + } +} + +/// int를 구문 분석하고, double을 int로 변환하거나, 숫자를 나눌 때 ~/ 연산을 사용하여 int를 유지합니다. 추측 게임도 해 봅시다. +example30() { + var gn, + tooHigh = false, + n, + n2 = (2.0).toInt(), + top = int.parse("123") ~/ n2, + bottom = 0; + top = top ~/ 6; + gn = new math.Random().nextInt(top + 1); /// +1은 nextInt top이 배타적이기 때문입니다. + print("Example30 Guess a number between 0 and ${top}"); + guessNumber(i) { + if (n == gn) { + print("Example30 Guessed right! The number is ${gn}"); + } else { + tooHigh = n > gn; + print("Example30 Number ${n} is too " + "${tooHigh ? 'high' : 'low'}. Try again"); + } + return n == gn; + } + + n = (top - bottom) ~/ 2; + while (!guessNumber(n)) { + if (tooHigh) { + top = n - 1; + } else { + bottom = n + 1; + } + n = bottom + ((top - bottom) ~/ 2); + } +} + +/// 선택적 위치 매개변수: +/// 매개변수는 대괄호 [ ]로 표시되며 대괄호로 묶인 매개변수는 선택 사항입니다. +example31() { + findVolume31(int length, int breath, [int? height]) { + print('length = $length, breath = $breath, height = $height'); + } + + findVolume31(10,20,30); // 유효 + findVolume31(10,20); // 또한 유효 +} + +/// 선택적 명명된 매개변수: +/// 매개변수는 중괄호 { }로 표시됩니다. +/// 중괄호로 묶인 매개변수는 선택 사항입니다. +/// 콜론 :으로 구분된 값을 할당하려면 매개변수 이름을 사용해야 합니다. +/// 중괄호로 묶인 매개변수 순서는 중요하지 않습니다. +/// 이러한 유형의 매개변수는 많은 매개변수를 가진 함수에 값을 전달할 때 혼동을 피하는 데 도움이 됩니다. +example32() { + findVolume32(int length, int breath, {int? height}) { + print('length = $length, breath = $breath, height = $height'); + } + + findVolume32(10,20,height:30);// 유효 & 매개변수 이름이 여기에 언급되어 있음을 알 수 있습니다. + findVolume32(10,20);// 또한 유효 +} + +/// 선택적 기본 매개변수: +/// 선택적 명명된 매개변수와 동일하지만 이 매개변수에 기본값을 할당할 수 있습니다. +/// 즉, 값이 전달되지 않으면 이 기본값이 사용됩니다. +example33() { + findVolume33(int length, int breath, {int height=10}) { + print('length = $length, breath = $breath, height = $height'); + } + + findVolume33(10,20,height:30);// 유효 + findVolume33(10,20);// 유효 +} + +/// Dart는 또한 Null 인식 연산자와 같은 기능을 추가했습니다. +var isBool = true; +var hasString = isBool ?? "default String"; + +/// 프로그램은 main 함수에 하나의 진입점만 가집니다. +/// 프로그램이 main 함수에서 실행되기 전에 외부 범위에서 아무것도 실행되지 않습니다. +/// 이는 더 빠른 로딩과 프로그램이 시작하는 데 필요한 것만 지연 로딩하는 데 도움이 됩니다. +main() { + print("Learn Dart in 15 minutes!"); + [ + example1, example2, example3, example4, example5, + example6, example7, example8, example9, example10, + example11, example12, example13, example14, example15, + example16, example17, example18, example19, example20, + example21, example22, example23, example24, example25, + example26, example27, example28, example29, + example30 // 이 주석을 추가하면 Dart 포맷터가 모든 항목을 새 줄에 넣는 것을 방지합니다. + ].forEach((ef) => ef()); +} +``` + +## 더 읽을거리 + +Dart는 포괄적인 웹사이트를 가지고 있습니다. API 참조, 튜토리얼, 기사 등을 포함하며, +유용한 DartPad(클라우드 기반 Dart 코딩 플레이그라운드)도 있습니다. +[https://dart.dev/](https://dart.dev) +[https://dartpad.dev/](https://dartpad.dev) \ No newline at end of file diff --git a/ko/dhall.md b/ko/dhall.md new file mode 100644 index 0000000000..e56a768f29 --- /dev/null +++ b/ko/dhall.md @@ -0,0 +1,348 @@ +--- +name: Dhall +filename: learndhall.dhall +contributors: + - ["Gabriel Gonzalez", "http://www.haskellforall.com/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Dhall은 YAML에 대한 반복적이지 않은 대안을 제공하는 프로그래밍 가능한 구성 언어입니다. + +Dhall은 JSON + 함수 + 유형 + 가져오기라고 생각할 수 있습니다. + +참고로 Dhall은 프로그래밍 가능하지만 튜링 완전하지는 않습니다. Dhall의 많은 기능은 더 강력한 안전 보장과 더 강력한 도구를 제공하기 위해 이 제한을 활용합니다. + +```haskell +-- 한 줄 주석 + +{- 여러 줄 주석 + + 유니코드도 괜찮습니다 🙂 + + 이 파일은 각 단계의 결과를 수집하는 큰 레코드로 평가되는 유효한 Dhall 표현식입니다. + + 파일을 해석하여 결과를 볼 수 있습니다: + + $ dhall --file learndhall.dhall + + {- 주석은 중첩될 수 있습니다 -} +-} + +let greeting = "Hello, world!" + +let fruits = "🍋🍓🍍🍉🍌" + +let interpolation = "맛있는 과일을 즐기세요: ${fruits}" + +let multilineText {- 인라인 주석도 작동합니다 -} = + '' + 선행 공백은 여러 줄 텍스트 리터럴에서 제거됩니다. + + 즉, 결과를 변경하지 않고 텍스트 리터럴을 자유롭게 들여쓰거나 내어쓸 수 있습니다. + + 리터럴 내의 상대 들여쓰기는 여전히 유지됩니다. + + 그 외에는 텍스트 리터럴이 "리터럴" YAML 여러 줄 문자열과 유사하게 그대로 유지됩니다. + '' + +let bool = True + +-- 바인딩에 대한 유형 주석은 선택 사항이지만 유용하므로 사용하겠습니다. +let annotation : Bool = True + +let renderedBool : Text = if bool then "True" else "False" + +-- 자연수는 음수가 아니며 부호가 없습니다. +let naturalNumber : Natural = 42 + +-- 정수는 음수일 수 있지만, 양수이더라도 명시적인 부호가 필요합니다. +let positiveInteger : Integer = +1 + +let negativeInteger : Integer = -12 + +let pi : Double = 3.14159265359 + +{- 백틱을 사용하여 식별자(예: 따옴표 및 공백)에 대해 더 넓은 문자 범위를 사용할 수 있습니다. +-} +let `Avogadro's Number` : Double = 6.0221409e+23 + +let origin : { x : Double, y : Double } = { x = 0.0, y = 0.0 } + +let somePrimes : List Natural = [ 2, 3, 5, 7, 11 ] + +{- 스키마는 유형과 동일합니다. + + 유형은 관례상 대문자로 시작하지만, 이 관례는 강제되지 않습니다. +-} +let Profile : Type + = { person : + { name : Text + , age : Natural + } + , address : + { country : Text + , state : Text + , city : Text + } + } + +let john : Profile = + { person = + { name = "John Doe" + , age = 67 + } + , address = + { country = "United States" + , state = "Pennsylvania" + , city = "Philadelphia" + } + } + +let philadelphia : Text = john.address.city + +{- Enum 대안은 관례상 대문자로 시작합니다. 이 관례는 강제되지 않습니다. +-} +let DNA : Type = < Adenine | Cytosine | Guanine | Thymine > + +let dnaSequence : List DNA = [ DNA.Thymine, DNA.Guanine, DNA.Guanine ] + +let compactDNASequence : List DNA = + let a = DNA.Adenine + let c = DNA.Cytosine + let g = DNA.Guanine + let t = DNA.Thymine + in [ c, t, t, a, t, c, g, g, c ] + +-- 각 대안당 하나의 필드를 가진 레코드를 제공하여 열거형을 변환할 수 있습니다. +let theLetterG : Text = + merge + { Adenine = "A" + , Cytosine = "C" + , Guanine = "G" + , Thymine = "T" + } + DNA.Guanine + +let presentOptionalValue : Optional Natural = Some 1 + +let absentOptionalValue : Optional Natural = None Natural + +let points : List { x : Double, y : Double } = + [ { x = 1.1, y = -4.2 } + , { x = 4.4, y = -3.0 } + , { x = 8.2, y = -5.5 } + ] + +{- `Natural -> List Natural`은 입력 유형이 `Natural`이고 + 출력 유형이 `List Natural`인 함수의 유형입니다. + + Dhall의 모든 함수는 익명 함수(일명 "람다")이며, + 선택적으로 이름을 지정할 수 있습니다. + + 예를 들어, 다음 함수는 이 Python 코드와 동일합니다: + + lambda n : [ n, n + 1 ] + + ... 그리고 이 JavaScript 코드: + + function (n) { return [ n, n + 1 ]; } +-} +let exampleFunction : Natural -> List Natural = + \(n : Natural) -> [ n, n + 1 ] + +-- Dhall은 유니코드 구문도 지원하지만, 이 튜토리얼에서는 ASCII를 사용합니다. +let unicodeFunction : Natural → List Natural = + λ(n : Natural) → [ n, n + 1 ] + +-- 함수 인수를 괄호로 묶을 필요가 없습니다. +let exampleFunctionApplication : List Natural = + exampleFunction 2 + +let functionOfMultipleArguments : Natural -> Natural -> List Natural = + \(x : Natural) -> \(y : Natural) -> [ x, y ] + +let functionAppliedToMultipleArguments : List Natural = + functionOfMultipleArguments 2 3 + +{- `exampleFunction`과 동일하지만 함수의 입력 유형에 이름을 지정했습니다: + "n" +-} +let namedArgumentType : forall (n : Natural) -> List Natural = + \(n : Natural) -> [ n, n + 1 ] + +{- 함수의 입력 유형에 이름을 지정하면 동일한 유형 내에서 나중에 해당 이름을 사용할 수 있습니다. + + 이렇게 하면 둘 이상의 입력 유형에 대해 작동하는 함수를 작성할 수 있습니다. + (일명 "다형성" 함수) +-} +let duplicate : forall (a : Type) -> a -> List a = + \(a : Type) -> \(x : a) -> [ x, x ] + +let duplicatedNumber : List Natural = + duplicate Natural 2 + +let duplicatedBool : List Bool = + duplicate Bool False + +{- 언어에는 다음과 같은 일부 내장 다형성 함수도 있습니다: + + List/head : forall (a : Type) -> List a -> Optional a +-} +let firstPrime : Optional Natural = List/head Natural somePrimes + +let functionOfARecord : { x : Natural, y : Natural } -> List Natural = + \(args : { x : Natural, y : Natural }) -> [ args.x, args.y ] + +let functionAppliedToARecord : List Natural = + functionOfARecord { x = 2, y = 5 } + +{- 모든 유형 변환은 명시적입니다. + + `Natural/show`는 다음 유형의 내장 함수입니다: + + Natural/show : Natural -> Text + + ... `Natural` 숫자를 `Text` 표현으로 변환합니다. +-} +let typeConversion : Natural -> Text = + \(age : Natural) -> "저는 ${Natural/show age}살입니다!" + +-- "템플릿"은 출력 유형이 `Text`인 함수와 동일합니다. +let mitLicense : { year : Natural, copyrightHolder : Text } -> Text = + \(args : { year : Natural, copyrightHolder : Text }) -> +'' +Copyright ${Natural/show args.year} ${args.copyrightHolder} + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +'' + +-- 템플릿 인스턴스화는 함수 적용과 동일합니다. +let templatedLicense : Text = + mitLicense { year = 2019, copyrightHolder = "Jane Smith" } + +{- URL로 표현식을 가져올 수 있습니다. + + 또한 Bash와 마찬가지로 로컬 파일 시스템에서 코드를 가져올 수 있습니다(표시되지 않음). + + 보안에 민감한 사용자는 의미론적 무결성 검사를 추가하여 원격으로 가져온 표현식을 고정할 수 있습니다. 이렇게 고정된 표현식을 조작하려는 시도는 인터프리터에서 거부됩니다. 그러나 가져온 콘텐츠의 동작 보존 리팩토링은 해시를 변경하지 않습니다. + + 이렇게 고정된 가져온 표현식은 콘텐츠 주소 지정 가능 저장소(일반적으로 `~/.cache/dhall` 아래)에 로컬로 캐시됩니다. +-} +let Natural/sum : List Natural -> Natural = + https://prelude.dhall-lang.org/Natural/sum + sha256:33f7f4c3aff62e5ecf4848f964363133452d420dcde045784518fb59fa970037 + +let twentyEight : Natural = Natural/sum somePrimes + +-- "패키지"는 가져올 수 있는 (중첩될 수 있는) 레코드와 동일합니다. +let Prelude = https://prelude.dhall-lang.org/package.dhall + +let false : Bool = Prelude.Bool.not True + +-- `as Text`를 가져오기에 추가하여 파일의 원시 내용을 가져올 수 있습니다. +let sourceCode : Text = https://prelude.dhall-lang.org/Bool/not as Text + +-- 환경 변수도 가져올 수 있습니다: +let presentWorkingDirectory = env:PWD as Text + +-- 가져오기가 실패할 경우 대체 표현식을 제공할 수 있습니다. +let home : Optional Text = Some env:HOME ? None Text + +-- 대체 표현식은 자체적으로 대체 가져오기를 포함할 수 있습니다. +let possiblyCustomPrelude = + env:DHALL_PRELUDE + ? https://prelude.dhall-lang.org/package.dhall + +{- `generate` 함수를 사용하여 10명의 빌드 사용자에 대한 구성을 자동 생성합니다: + + Prelude.List.generate + : Natural -> forall (a : Type) -> (Natural -> a) -> List a +-} +let buildUsers = + let makeUser = \(user : Text) -> + let home = "/home/${user}" + let privateKey = "${home}/.ssh/id_ed25519" + let publicKey = "${privateKey}.pub" + in { home = home + , privateKey = privateKey + , publicKey = publicKey + } + + let buildUser = + \(index : Natural) -> makeUser "build${Natural/show index}" + + let Config = + { home : Text + , privateKey : Text + , publicKey : Text + } + + in Prelude.List.generate 10 Config buildUser + +-- 모든 결과를 최종 레코드에 표시합니다. +in { greeting = greeting + , fruits = fruits + , interpolation = interpolation + , multilineText = multilineText + , bool = bool + , annotation = annotation + , renderedBool = renderedBool + , naturalNumber = naturalNumber + , positiveInteger = positiveInteger + , negativeInteger = negativeInteger + , pi = pi + , `Avogadro's Number` = `Avogadro's Number` + , origin = origin + , somePrimes = somePrimes + , john = john + , philadelphia = philadelphia + , dnaSequence = dnaSequence + , compactDNASequence = compactDNASequence + , theLetterG = theLetterG + , presentOptionalValue = presentOptionalValue + , absentOptionalValue = absentOptionalValue + , points = points + , exampleFunction = exampleFunction + , unicodeFunction = unicodeFunction + , exampleFunctionApplication = exampleFunctionApplication + , functionOfMultipleArguments = functionOfMultipleArguments + , functionAppliedToMultipleArguments = functionAppliedToMultipleArguments + , namedArgumentType = namedArgumentType + , duplicate = duplicate + , duplicatedNumber = duplicatedNumber + , duplicatedBool = duplicatedBool + , firstPrime = firstPrime + , functionOfARecord = functionOfARecord + , functionAppliedToARecord = functionAppliedToARecord + , typeConversion = typeConversion + , mitLicense = mitLicense + , templatedLicense = templatedLicense + , twentyEight = twentyEight + , false = false + , sourceCode = sourceCode + , presentWorkingDirectory = presentWorkingDirectory + , home = home + , buildUsers = buildUsers + } +``` + +더 자세히 알아보려면 공식 웹사이트를 방문하십시오. 브라우저에서 언어를 직접 사용해 볼 수도 있습니다: + +* [https://dhall-lang.org](http://dhall-lang.org/) \ No newline at end of file diff --git a/ko/directx9.md b/ko/directx9.md new file mode 100644 index 0000000000..806309d126 --- /dev/null +++ b/ko/directx9.md @@ -0,0 +1,813 @@ +--- +category: framework +name: DirectX 9 +filename: learndirectx9.cpp +contributors: + - ["Simon Deitermann", "s.f.deitermann@t-online.de"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +**Microsoft DirectX**는 멀티미디어, 특히 게임 프로그래밍 및 비디오와 관련된 작업을 Microsoft 플랫폼에서 처리하기 위한 애플리케이션 프로그래밍 인터페이스(API) 모음입니다. 원래 이러한 API의 이름은 모두 Direct로 시작했습니다(예: Direct3D, DirectDraw, DirectMusic, DirectPlay, DirectSound 등). [...] Direct3D(DirectX 내의 3D 그래픽 API)는 Microsoft Windows 및 Xbox 콘솔 라인용 비디오 게임 개발에 널리 사용됩니다.[1] + +이 튜토리얼에서는 DirectX 9에 초점을 맞출 것입니다. DirectX 9는 후속 버전만큼 저수준은 아니지만, 그래픽 하드웨어 작동 방식에 매우 익숙한 프로그래머를 대상으로 합니다. Direct3D를 배우기 위한 훌륭한 시작점입니다. 이 튜토리얼에서는 창 처리 및 DirectX 2010 SDK에 Win32-API를 사용할 것입니다. + +## 창 생성 + +```cpp +#include + +bool _running{ false }; + +LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + // 들어오는 메시지 처리. + switch (msg) { + // 사용자가 창을 닫으려고 하면 running을 false로 설정합니다. + case WM_DESTROY: + _running = false; + PostQuitMessage(0); + break; + } + // 처리된 이벤트 반환. + return DefWindowProc(hWnd, msg, wParam, lParam); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, int nCmdShow) { + // 사용할 창 속성 설정. + WNDCLASSEX wndEx{ }; + wndEx.cbSize = sizeof(WNDCLASSEX); // 구조체 크기 + wndEx.style = CS_VREDRAW | CS_HREDRAW; // 클래스 스타일 + wndEx.lpfnWndProc = WndProc; // 창 프로시저 + wndEx.cbClsExtra = 0; // 추가 메모리 (구조체) + wndEx.cbWndExtra = 0; // 추가 메모리 (창) + wndEx.hInstance = hInstance; // 모듈 인스턴스 + wndEx.hIcon = LoadIcon(nullptr, IDI_APPLICATION); // 아이콘 + wndEx.hCursor = LoadCursor(nullptr, IDC_ARROW); // 커서 + wndEx.hbrBackground = (HBRUSH) COLOR_WINDOW; // 배경색 + wndEx.lpszMenuName = nullptr; // 메뉴 이름 + wndEx.lpszClassName = "DirectXClass"; // 클래스 이름 등록 + wndEx.hIconSm = nullptr; // 작은 아이콘 (작업 표시줄) + // 창 생성을 위해 생성된 클래스 등록. + RegisterClassEx(&wndEx); + // 새 창 핸들 생성. + HWND hWnd{ nullptr }; + // 등록된 클래스를 사용하여 새 창 핸들 생성. + hWnd = CreateWindow("DirectXClass", // 등록된 클래스 + "directx window", // 창 제목 + WS_OVERLAPPEDWINDOW, // 창 스타일 + 50, 50, // x, y (위치) + 1024, 768, // 너비, 높이 (크기) + nullptr, // 부모 창 + nullptr, // 메뉴 + hInstance, // 모듈 인스턴스 + nullptr); // 정보 구조체 + // 창 핸들이 생성되었는지 확인. + if (!hWnd) + return -1; + // 새 창 표시 및 업데이트. + ShowWindow(hWnd, nCmdShow); + UpdateWindow(hWnd); + // 게임 루프를 시작하고 들어오는 메시지를 창 프로시저로 보냅니다. + _running = true; + MSG msg{ }; + while (_running) { + while (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + return 0; +} +``` + +이렇게 하면 이동, 크기 조정 및 닫을 수 있는 창이 생성됩니다. + +## Direct3D 초기화 + +```cpp +// DirectX 9 구조체 및 함수를 포함합니다. +// "d3d9.lib" 및 "d3dx9.lib"를 링크하는 것을 잊지 마십시오. +// "d3dx9.lib"의 경우 DirectX SDK (2010년 6월)가 필요합니다. +// 서브시스템을 Windows로 설정하는 것을 잊지 마십시오. +#include +#include +// COM 객체를 자동으로 해제하는 스마트 포인터인 ComPtr를 포함합니다. +#include +using namespace Microsoft::WRL; +// 다음으로 필요한 Direct3D9 인터페이스 구조체를 정의합니다. +ComPtr _d3d{ }; +ComPtr _device{ }; +``` + +모든 인터페이스가 선언되었으므로 이제 Direct3D를 초기화할 수 있습니다. + +```cpp +bool InitD3D(HWND hWnd) { + // 창 사각형의 크기를 저장합니다. + RECT clientRect{ }; + GetClientRect(hWnd, &clientRect); + // Direct3D 초기화 + _d3d = Direct3DCreate9(D3D_SDK_VERSION); + // 디스플레이 모드를 가져옵니다. 이 모드는 창 형식으로 사용됩니다. + D3DDISPLAYMODE displayMode{ }; + _d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, // 기본 그래픽 카드 사용 + &displayMode); // 디스플레이 모드 포인터 + // 다음으로 일부 프레젠테이션 매개변수를 설정해야 합니다. + D3DPRESENT_PARAMETERS pp{ }; + pp.BackBufferWidth = clientRect.right; // 너비는 창 너비 + pp.BackBufferHeight = clientRect.bottom; // 높이는 창 높이 + pp.BackBufferFormat = displayMode.Format; // 어댑터 형식 사용 + pp.BackBufferCount = 1; // 1개의 백 버퍼 (기본값) + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; // 프레젠테이션 후 버림 + pp.hDeviceWindow = hWnd; // 연결된 창 핸들 + pp.Windowed = true; // 창 모드로 표시 + pp.Flags = 0; // 특수 플래그 없음 + // 모든 것이 성공했는지 확인하기 위해 메서드 결과를 저장할 변수입니다. + HRESULT result{ }; + result = _d3d->CreateDevice(D3DADAPTER_DEFAULT, // 기본 그래픽 카드 사용 + D3DDEVTYPE_HAL, // 하드웨어 가속 사용 + hWnd, // 창 핸들 + D3DCREATE_HARDWARE_VERTEXPROCESSING, + // 정점은 하드웨어에서 처리됩니다. + &pp, // 현재 매개변수 + &_device); // 장치를 저장할 구조체 + // 장치 생성이 실패하면 false를 반환합니다. + // 반환 줄에 중단점을 설정하는 것이 도움이 됩니다. + if (FAILED(result)) + return false; + // 그릴 영역에 대한 정보를 담는 뷰포트를 생성합니다. + D3DVIEWPORT9 viewport{ }; + viewport.X = 0; // 왼쪽 상단 모서리에서 시작 + viewport.Y = 0; // .. + viewport.Width = clientRect.right; // 전체 창 사용 + viewport.Height = clientRect.bottom; // .. + viewport.MinZ = 0.0f; // 최소 보기 거리 + viewport.MaxZ = 100.0f; // 최대 보기 거리 + // 생성된 뷰포트 적용. + result = _device->SetViewport(&viewport); + // 항상 실패 여부를 확인하십시오. + if (FAILED(result)) + return false; + // 모든 것이 성공했으므로 true를 반환합니다. + return true; +} +// ... +// WinMain 함수에서 초기화 함수를 호출합니다. +// ... +// Direct3D 초기화가 성공했는지 확인하고, 그렇지 않으면 애플리케이션을 종료합니다. +if (!InitD3D(hWnd)) + return -1; + +MSG msg{ }; +while (_running) { + while (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + // 지정된 색상으로 렌더링 대상을 지웁니다. + _device->Clear(0, // 지울 사각형 수 + nullptr, // 전체 창을 지울 것을 나타냅니다. + D3DCLEAR_TARGET, // 모든 렌더링 대상을 지웁니다. + D3DXCOLOR{ 1.0f, 0.0f, 0.0f, 1.0f }, // 색상 (빨간색) + 0.0f, // 깊이 버퍼 지우기 값 + 0); // 스텐실 버퍼 지우기 값 + // ... + // 여기에 그리기 작업이 들어갑니다. + // ... + // 전면 및 후면 버퍼를 뒤집습니다. + _device->Present(nullptr, // 소스 사각형 없음 + nullptr, // 대상 사각형 없음 + nullptr, // 현재 창 핸들을 변경하지 않습니다. + nullptr); // 거의 항상 nullptr +} +// ... +``` + +이제 창이 밝은 빨간색으로 표시되어야 합니다. + +## 정점 버퍼 + +삼각형의 정점을 저장할 정점 버퍼를 만들어 보겠습니다. + +```cpp +// 먼저 파일에 include를 추가해야 합니다. +#include +// 먼저 정점 버퍼를 담을 새 ComPtr를 선언합니다. +ComPtr _vertexBuffer{ }; +// std::vector의 바이트 크기를 계산하는 함수를 정의해 보겠습니다. +template +unsigned int GetByteSize(const std::vector& vec) { + return sizeof(vec[0]) * vec.size(); +} +// 정점 구조체의 내용을 설명하는 "유연한 정점 형식"을 정의합니다. +// 정의된 색상을 확산 색상으로 사용합니다. +const unsigned long VertexStructFVF = D3DFVF_XYZ | D3DFVF_DIFFUSE; +// 버퍼가 담을 정점 데이터를 나타내는 구조체를 정의합니다. +struct VStruct { + float x, y, z; // 3D 위치 저장 + D3DCOLOR color; // 색상 저장 +}; +// 정점 버퍼를 생성하는 새 함수를 선언합니다. +IDirect3DVertexBuffer9* CreateBuffer(const std::vector& vertices) { + // 반환할 버퍼를 선언합니다. + IDirect3DVertexBuffer9* buffer{ }; + HRESULT result{ }; + result = _device->CreateVertexBuffer( + GetByteSize(vertices), // 벡터 크기(바이트) + 0, // 데이터 사용량 + VertexStructFVF, // 구조체의 FVF + D3DPOOL_DEFAULT, // 버퍼의 기본 풀 사용 + &buffer, // 수신 버퍼 + nullptr); // 특수 공유 핸들 + // 버퍼가 성공적으로 생성되었는지 확인합니다. + if (FAILED(result)) + return nullptr; + // 정점 데이터를 복사하기 위한 데이터 포인터를 생성합니다. + void* data{ }; + // 데이터 저장을 위한 버퍼를 얻기 위해 버퍼를 잠급니다. + result = buffer->Lock(0, // 바이트 오프셋 + GetByteSize(vertices), // 잠글 크기 + &data, // 수신 데이터 포인터 + 0); // 특수 잠금 플래그 + // 버퍼가 성공적으로 잠겼는지 확인합니다. + if (FAILED(result)) + return nullptr; + // C 표준 라이브러리 memcpy를 사용하여 정점 데이터를 복사합니다. + memcpy(data, vertices.data(), GetByteSize(vertices)); + buffer->Unlock(); + // 렌더링에 사용할 FVF Direct3D를 설정합니다. + _device->SetFVF(VertexStructFVF); + // 모든 것이 성공했으면 채워진 정점 버퍼를 반환합니다. + return buffer; +} +``` + +**WinMain**에서 Direct3D 초기화 후 새 함수를 호출할 수 있습니다. + +```cpp +// ... +if (!InitD3D(hWnd)) + return -1; +// 삼각형을 그리는 데 필요한 정점을 정의합니다. +// 값은 시계 방향으로 선언됩니다. 그렇지 않으면 Direct3D가 컬링합니다. +// 컬링을 비활성화하려면 다음을 호출하십시오: +// _device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); +std::vector vertices { + // 왼쪽 하단 + VStruct{ -1.0f, -1.0f, 1.0f, D3DXCOLOR{ 1.0f, 0.0f, 0.0f, 1.0f } }, + // 왼쪽 상단 + VStruct{ -1.0f, 1.0f, 1.0f, D3DXCOLOR{ 0.0f, 1.0f, 0.0f, 1.0f } }, + // 오른쪽 상단 + VStruct{ 1.0f, 1.0f, 1.0f, D3DXCOLOR{ 0.0f, 0.0f, 1.0f, 1.0f } } +}; +// 정점 버퍼를 생성하거나 애플리케이션을 종료합니다. +if (!(_vertexBuffer = CreateBuffer(vertices))) + return -1; +// ... +``` + +## 변환 + +정점 버퍼를 사용하여 기본 요소를 그리기 전에 먼저 행렬을 설정해야 합니다. + +```cpp +// 행렬 변환을 위한 새 함수를 만들어 보겠습니다. +bool SetupTransform() { + // 월드 공간을 뷰 공간으로 변환하는 뷰 행렬을 생성합니다. + D3DXMATRIX view{ }; + // 왼손 좌표계를 사용합니다. + D3DXMatrixLookAtLH( + &view, // 수신 행렬 + &D3DXVECTOR3{ 0.0f, 0.0f, -20.0f }, // "카메라" 위치 + &D3DXVECTOR3{ 0.0f, 0.0f, 0.0f }, // 볼 위치 + &D3DXVECTOR3{ 0.0f, 1.0f, 0.0f }); // 양의 y축이 위쪽 + HRESULT result{ }; + result = _device->SetTransform(D3DTS_VIEW, &view); // 뷰 행렬 적용 + if (FAILED(result)) + return false; + // 뷰 절두체를 정의하는 투영 행렬을 생성합니다. + // 뷰 공간을 투영 공간으로 변환합니다. + D3DXMATRIX projection{ }; + // 왼손 좌표계를 사용하여 원근 투영을 생성합니다. + D3DXMatrixPerspectiveFovLH( + &projection, // 수신 행렬 + D3DXToRadian(60.0f), // 라디안 단위의 시야각 + 1024.0f / 768.0f, // 종횡비 (너비 / 높이) + 0.0f, // 최소 보기 거리 + 100.0f); // 최대 보기 거리 + result = _device->SetTransform(D3DTS_PROJECTION, &projection); + if (FAILED(result)) + return false; + // 렌더링하려는 것을 볼 수 있도록 조명을 비활성화합니다. + result = _device->SetRenderState(D3DRS_LIGHTING, false); + // 뷰 및 투영 행렬이 성공적으로 적용되었으므로 true를 반환합니다. + return true; +} +// ... +// WinMain 함수에서 이제 변환 함수를 호출할 수 있습니다. +// ... +if (!(_vertexBuffer = CreateVertexBuffer(vertices))) + return -1; +// 변환 설정 함수 호출. +if (!SetupTransform()) + return -1; +// ... +``` + +## 렌더링 + +이제 모든 설정이 완료되었으므로 3D 공간에 첫 번째 2D 삼각형을 그리기 시작할 수 있습니다. + +```cpp +// ... +if (!SetupTransform()) + return -1; +// 먼저 정점 버퍼를 데이터 스트림에 바인딩해야 합니다. +HRESULT result{ }; +result = _device->SetStreamSource(0, // 기본 스트림 사용 + _vertexBuffer.Get(), // 정점 버퍼 전달 + 0, // 오프셋 없음 + sizeof(VStruct)); // 정점 구조체 크기 +if (FAILED(result)) + return -1; + +// 월드 변환 행렬을 생성하고 항등 행렬로 설정합니다. +D3DXMATRIX world{ }; +D3DXMatrixIdentity(&world); +// x축으로 10, y축으로 10만큼 스케일링하고 z축 방향을 유지하는 스케일링 행렬을 생성합니다. +D3DXMATRIX scaling{ }; +D3DXMatrixScaling(&scaling, // 스케일링할 행렬 + 10, // x 스케일링 + 10, // y 스케일링 + 1); // z 스케일링 +// 기본 요소의 현재 회전을 저장하는 회전 행렬을 생성합니다. +// 현재 회전 행렬을 항등 행렬로 설정합니다. +D3DXMATRIX rotation{ }; +D3DXMatrixIdentity(&rotation); +// 이제 스케일링 및 회전 행렬을 곱하고 결과를 +// 월드 행렬에 저장합니다. +D3DXMatrixMultiply(&world, // 대상 행렬 + &scaling, // 행렬 1 + &rotation); // 행렬 2 +// 현재 월드 행렬 적용. +_device->SetTransform(D3DTS_WORLD, &world); +// 회전할 때 기본 요소의 뒷면을 볼 수 있도록 컬링을 비활성화합니다. +_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); +// 기본 컬링 모드는 D3DCULL_CW입니다. +// 곱셈에 회전 행렬을 사용한 후 +// 약간 회전하도록 설정할 수 있습니다. +// D3DXToRadian() 함수는 도를 라디안으로 변환합니다. +D3DXMatrixRotationY(&rotation, // 회전할 행렬 + D3DXToRadian(0.5f)); // 라디안 단위의 회전 각도 + +MSG msg{ }; + while (_running) { + // ... + _device->Clear(0, nullptr, D3DCLEAR_TARGET, + D3DXCOLOR{ 0.0f, 0.0f, 0.0f, 1.0f }, 0.0f, 0); + // 모든 설정이 완료되었으므로 그리기 함수를 호출할 수 있습니다. + _device->BeginScene(); + _device->DrawPrimitive(D3DPT_TRIANGLELIST, // 기본 유형 + 0, // 시작 정점 + 1); // 기본 요소 수 + _device->EndScene(); + + _device->Present(nullptr, nullptr, nullptr, nullptr); + // 월드 행렬에 회전을 추가하기 위해 월드 행렬에 회전 행렬을 계속 곱할 수 있습니다. + D3DXMatrixMultiply(&world, &world, &rotation); + // 수정된 월드 행렬 업데이트. + _device->SetTransform(D3DTS_WORLD, &world); + // ... +``` + +이제 20단위 떨어진 곳에서 원점을 중심으로 회전하는 10x10 단위의 색상 삼각형이 표시되어야 합니다.
+전체 작동 코드는 여기에서 찾을 수 있습니다: [DirectX - 1](https://pastebin.com/YkSF2rkk) + +## 인덱싱 + +많은 정점을 공유하는 기본 요소를 더 쉽게 그릴 수 있도록 인덱싱을 사용할 수 있습니다. 이렇게 하면 고유한 정점만 선언하고 호출 순서를 다른 배열에 넣을 수 있습니다. + +```cpp +// 먼저 인덱스 버퍼를 위한 새 ComPtr를 선언해야 합니다. +ComPtr _indexBuffer{ }; +// ... +// std::vector에서 인덱스 버퍼를 생성하는 함수를 선언합니다. +IDirect3DIndexBuffer9* CreateIBuffer(std::vector& indices) { + IDirect3DIndexBuffer9* buffer{ }; + HRESULT result{ }; + result = _device->CreateIndexBuffer( + GetByteSize(indices), // 벡터 크기(바이트) + 0, // 데이터 사용량 + D3DFMT_INDEX32, // 형식은 32비트 int + D3DPOOL_DEFAULT, // 기본 풀 + &buffer, // 수신 버퍼 + nullptr); // 특수 공유 핸들 + if (FAILED(result)) + return nullptr; + // 버퍼 데이터에 대한 데이터 포인터를 생성합니다. + void* data{ }; + result = buffer->Lock(0, // 바이트 오프셋 + GetByteSize(indices), // 바이트 크기 + &data, // 수신 데이터 포인터 + 0); // 특수 잠금 플래그 + if (FAILED(result)) + return nullptr; + // 인덱스 데이터를 복사하고 복사 후 잠금을 해제합니다. + memcpy(data, indices.data(), GetByteSize(indices)); + buffer->Unlock(); + // 채워진 인덱스 버퍼를 반환합니다. + return buffer; +} +// ... +// WinMain에서 이제 정점 데이터를 변경하고 새 인덱스 데이터를 생성할 수 있습니다. +// ... +std::vector vertices { + VStruct{ -1.0f, -1.0f, 1.0f, D3DXCOLOR{ 1.0f, 0.0f, 0.0f, 1.0f } }, + VStruct{ -1.0f, 1.0f, 1.0f, D3DXCOLOR{ 0.0f, 1.0f, 0.0f, 1.0f } }, + VStruct{ 1.0f, 1.0f, 1.0f, D3DXCOLOR{ 0.0f, 0.0f, 1.0f, 1.0f } }, + // 오른쪽 하단에 정점 추가. + VStruct{ 1.0f, -1.0f, 1.0f, D3DXCOLOR{ 1.0f, 1.0f, 0.0f, 1.0f } } +}; +// 인덱스 데이터를 선언합니다. 여기서는 두 개의 삼각형으로 사각형을 만듭니다. +std::vector indices { + 0, 1, 2, // 첫 번째 삼각형 (왼쪽 하단 -> 왼쪽 상단 -> 오른쪽 상단) + 0, 2, 3 // 두 번째 삼각형 (왼쪽 하단 -> 오른쪽 상단 -> 오른쪽 하단) +}; +// ... +// 이제 "CreateIBuffer" 함수를 호출하여 인덱스 버퍼를 생성합니다. +// ... +if (!(_indexBuffer = CreateIBuffer(indices))) + return -1; +// ... +// 정점 버퍼를 바인딩한 후 인덱스 버퍼를 바인딩하여 +// 인덱싱된 렌더링을 사용해야 합니다. +result = _device->SetStreamSource(0, _vertexBuffer.Get(), 0, sizeof(VStruct)); +if (FAILED(result)) + return -1; +// 인덱스 데이터를 기본 데이터 스트림에 바인딩합니다. +result = _device->SetIndices(_indexBuffer.Get()) +if (FAILED(result)) + return -1; +// ... +// 이제 "DrawPrimitive" 함수를 인덱싱된 버전으로 교체합니다. +_device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, // 기본 유형 + 0, // 기본 정점 인덱스 + 0, // 최소 인덱스 + indices.size(), // 정점 수 + 0, // 인덱스 버퍼 시작 + 2); // 기본 요소 수 +// ... +``` + +이제 2개의 삼각형으로 구성된 색상 사각형이 표시되어야 합니다. "DrawIndexedPrimitive" 메서드의 기본 요소 수를 1로 설정하면 첫 번째 삼각형만 렌더링되고, 인덱스 버퍼의 시작을 3으로 설정하고 기본 요소 수를 1로 설정하면 두 번째 삼각형만 렌더링됩니다.
+전체 작동 코드는 여기에서 찾을 수 있습니다: [DirectX - 2](https://pastebin.com/yWBPWPRG) + +## 정점 선언 + +오래된 "유연한 정점 형식"을 사용하는 대신 정점 선언을 사용해야 합니다. FVF 선언은 내부적으로 정점 선언으로 변환되기 때문입니다. + +```cpp +// 먼저 다음 줄을 제거해야 합니다: +const unsigned long VertexStructFVF = D3DFVF_XYZ | D3DFVF_DIFFUSE; +// 그리고 +_device->SetFVF(VertexStructFVF); +// ... +// 정점 버퍼 생성 FVF 플래그도 변경해야 합니다. +result = _device->CreateVertexBuffer( + GetByteSize(vertices), + 0, + 0, // <- 0은 정점 선언을 사용함을 나타냅니다. + D3DPOOL_DEFAULT, + &buffer, + nullptr); +// 다음으로 새 ComPtr를 선언해야 합니다. +ComPtr _vertexDecl{ }; +// ... +result = _device->SetIndices(_indexBuffer.Get()); +if (FAILED(result)) + return -1; +// 이제 정점 선언을 선언하고 적용해야 합니다. +// 정점 선언을 구성하는 정점 요소 벡터를 생성합니다. +std::vector vertexDeclDesc { + { 0, // 스트림 인덱스 + 0, // 구조체 시작부터의 바이트 오프셋 + D3DDECLTYPE_FLOAT3, // 데이터 유형 (3D float 벡터) + D3DDECLMETHOD_DEFAULT, // 테셀레이터 작업 + D3DDECLUSAGE_POSITION, // 데이터 사용량 + 0 }, // 인덱스 (동일한 유형의 여러 사용) + { 0, + 12, // 바이트 오프셋 (3 * sizeof(float) 바이트) + D3DDECLTYPE_D3DCOLOR, + D3DDECLMETHOD_DEFAULT, + D3DDECLUSAGE_COLOR, + 0 }, + D3DDECL_END() // 정점 선언의 끝을 표시합니다. +}; +// 벡터를 정의한 후 이를 사용하여 정점 선언을 생성할 수 있습니다. +result = _device->CreateVertexDeclaration( + vertexDeclDesc.data(), // 정점 요소 배열 + &_vertexDecl); // 수신 포인터 +if (FAILED(result)) + return -1; +// 생성된 정점 선언 적용. +_device->SetVertexDeclaration(_vertexDecl.Get()); +// ... +``` + +## 셰이더 + +Direct3D 9의 최대 셰이더 모델은 셰이더 모델 3.0입니다. 모든 최신 그래픽 카드가 이를 지원해야 하지만, 기능 확인이 가장 좋습니다. + +```cpp +// ... +_device->SetVertexDeclaration(_vertexDecl.Get()); +// 먼저 장치 기능을 요청해야 합니다. +D3DCAPS9 deviceCaps{ }; +_device->GetDeviceCaps(&deviceCaps); +// 이제 정점 셰이더에 대해 셰이더 모델 3.0이 지원되는지 확인합니다. +if (deviceCaps.VertexShaderVersion < D3DVS_VERSION(3, 0)) + return -1; +// 픽셀 셰이더도 마찬가지입니다. +if (deviceCaps.PixelShaderVersion < D3DPS_VERSION(3, 0)) + return -1; +``` + +이제 셰이더 모델 3.0이 지원됨을 확인했으므로 정점 및 픽셀 셰이더 파일을 생성해 보겠습니다. +DirectX 9는 C와 유사한 셰이더 언어인 HLSL(**High Level Shading Language**)을 도입하여 +셰이더 프로그래밍을 훨씬 단순화했습니다. DirectX 8에서는 셰이더 어셈블리로만 셰이더를 작성할 수 있었습니다. +두 가지 기본 셰이더를 만들어 보겠습니다. + +**정점 셰이더** + +```cpp +// SetTransform() 메서드를 사용하여 고정 함수 파이프라인에 설정한 행렬을 나타내는 3개의 4x4 float 행렬입니다. +float4x4 projectionMatrix; +float4x4 viewMatrix; +float4x4 worldMatrix; +// 정점 셰이더에 대한 입력 구조체입니다. +// 위치를 포함하는 3D float 벡터와 +// 색상을 포함하는 4D float 벡터를 가집니다. +struct VS_INPUT { + float3 position : POSITION; + float4 color : COLOR; +}; +// 정점 셰이더의 출력 구조체로, 픽셀 셰이더로 전달됩니다. +struct VS_OUTPUT { + float4 position : POSITION; + float4 color : COLOR; +}; +// 정점 셰이더의 main 함수는 픽셀 셰이더로 보내는 출력을 반환하고 +// 입력을 매개변수로 받습니다. +VS_OUTPUT main(VS_INPUT input) { + // 정점 셰이더가 반환하는 빈 구조체를 선언합니다. + VS_OUTPUT output; + // 출력 위치를 입력 위치로 설정하고 + // w-구성 요소를 1로 설정합니다. 입력 위치는 3D 벡터이고 + // 출력 위치는 4D 벡터이기 때문입니다. + output.position = float4(input.position, 1.0f); + // 출력 위치를 월드, 뷰 및 투영 행렬로 단계별로 곱합니다. + output.position = mul(output.position, worldMatrix); + output.position = mul(output.position, viewMatrix); + output.position = mul(output.position, projectionMatrix); + // 입력 색상을 변경하지 않고 픽셀 셰이더로 전달합니다. + output.color = input.color; + // 출력 구조체를 픽셀 셰이더로 반환합니다. + // 위치 값은 자동으로 정점 위치로 사용됩니다. + return output; +} +``` + +**픽셀 셰이더** + +```cpp +// 픽셀 셰이더 입력 구조체는 정점 셰이더 출력과 동일해야 합니다! +struct PS_INPUT { + float4 position : POSITION; + float4 color : COLOR; +}; +// 픽셀 셰이더는 정점 색상을 나타내는 4D 벡터를 간단히 반환합니다. +// 정점 셰이더와 마찬가지로 입력을 매개변수로 받습니다. +// 올바르게 해석되도록 출력 시맨틱을 color로 선언해야 합니다. +float4 main(PS_INPUT input) : COLOR { + return input.color; +} +``` + +시맨틱에 대한 자세한 내용은 다음을 참조하십시오: [DirectX - Semantics](https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics#vertex-shader-semantics) + +이제 코드에 상당한 변경을 가해야 합니다. + +```cpp +ComPtr _device{ }; +ComPtr _vertexBuffer{ }; +ComPtr _indexBuffer{ }; +ComPtr _vertexDecl{ }; +// 정점 및 픽셀 셰이더를 위한 ComPtr와 +// 정점 셰이더의 상수(행렬)를 위한 ComPtr를 추가해야 합니다. +ComPtr _vertexShader{ }; +ComPtr _pixelShader{ }; +ComPtr _vertexTable{ }; +// WinMain 및 SetupTransform에서 사용하므로 월드 및 회전 행렬을 전역으로 선언합니다. +D3DXMATRIX _worldMatrix{ }; +D3DXMATRIX _rotationMatrix{ }; +// ... +bool SetupTransform() { + // 월드 및 회전 행렬을 항등 행렬로 설정합니다. + D3DXMatrixIdentity(&_worldMatrix); + D3DXMatrixIdentity(&_rotationMatrix); + + D3DXMATRIX scaling{ }; + D3DXMatrixScaling(&scaling, 10, 10, 1); + D3DXMatrixMultiply(&_worldMatrix, &scaling, &_rotationMatrix); + // 스케일링 및 회전 행렬을 곱한 후 정점 셰이더의 상수 테이블에서 메서드를 사용하여 셰이더에 전달해야 합니다. + HRESULT result{ }; + result = _vertexTable->SetMatrix( + _device.Get(), // direct3d 장치 + "worldMatrix", // 셰이더의 행렬 이름 + &_worldMatrix); // 행렬에 대한 포인터 + if (FAILED(result)) + return false; + + D3DXMATRIX view{ }; + D3DXMatrixLookAtLH(&view, &D3DXVECTOR3{ 0.0f, 0.0f, -20.0f }, + &D3DXVECTOR3{ 0.0f, 0.0f, 0.0f }, &D3DXVECTOR3{ 0.0f, 1.0f, 0.0f }); + // 뷰 행렬도 마찬가지입니다. + result = _vertexTable->SetMatrix( + _device.Get(), // direct 3d 장치 + "viewMatrix", // 행렬 이름 + &view); // 행렬 + if (FAILED(result)) + return false; + + D3DXMATRIX projection{ }; + D3DXMatrixPerspectiveFovLH(&projection, D3DXToRadian(60.0f), + 1024.0f / 768.0f, 0.0f, 100.0f); + // 투영 행렬도 마찬가지입니다. + result = _vertexTable->SetMatrix( + _device.Get(), + "projectionMatrix", + &projection); + if (FAILED(result)) + return false; + + D3DXMatrixRotationY(&_rotationMatrix, D3DXToRadian(0.5f)); + return true; +} +// ... +// 정점 및 인덱스 버퍼 생성 및 초기화는 변경되지 않습니다. +// ... +// 셰이더 모델 3.0이 사용 가능한지 확인한 후 셰이더를 컴파일하고 생성해야 합니다. +// 컴파일된 셰이더 코드를 저장할 두 개의 임시 버퍼를 선언합니다. +ID3DXBuffer* vertexShaderBuffer{ }; +ID3DXBuffer* pixelShaderBuffer{ }; +result = D3DXCompileShaderFromFile("vertex.hlsl", // 셰이더 이름 + nullptr, // 매크로 정의 + nullptr, // 특수 포함 + "main", // 진입점 이름 + "vs_3_0", // 셰이더 모델 버전 + 0, // 특수 플래그 + &vertexShaderBuffer, // 코드 버퍼 + nullptr, // 오류 메시지 + &_vertexTable); // 상수 테이블 +if (FAILED(result)) + return -1; +// 정점 셰이더 컴파일 후 픽셀 셰이더 컴파일. +result = D3DXCompileShaderFromFile("pixel.hlsl", + nullptr, + nullptr, + "main", + "ps_3_0", // 픽셀 셰이더 모델 3.0 + 0, + &pixelShaderBuffer, + nullptr, + nullptr); // 상수 테이블 필요 없음 +if (FAILED(result)) + return -1; +// 코드 버퍼에서 정점 셰이더를 생성합니다. +result = _device->CreateVertexShader( + (DWORD*)vertexShaderBuffer->GetBufferPointer(), // 코드 버퍼 + &_vertexShader); // 정점 셰이더 포인터 +if (FAILED(result)) + return -1; + +result = _device->CreatePixelShader( + (DWORD*)pixelShaderBuffer->GetBufferPointer(), + &_pixelShader); +if (FAILED(result)) + return -1; +// 셰이더가 생성된 후 임시 코드 버퍼를 해제합니다. +vertexShaderBuffer->Release(); +pixelShaderBuffer->Release(); +// 정점 및 픽셀 셰이더 적용. +_device->SetVertexShader(_vertexShader.Get()); +_device->SetPixelShader(_pixelShader.Get()); +// 셰이더가 설정된 후 변환을 적용합니다. +if (!SetupTransform()) + return -1; +// 조명 렌더링 상태를 설정하는 호출을 제거할 수도 있습니다. +_device->SetRenderState(D3DRS_LIGHTING, false); +``` + +전체 코드는 여기에서 찾을 수 있습니다: [DirectX - 3](https://pastebin.com/y4NrvawY) + +## 텍스처링 + +```cpp +// 먼저 텍스처를 위한 ComPtr를 선언해야 합니다. +ComPtr _texture{ }; +// 그런 다음 정점 구조체를 변경해야 합니다. +struct VStruct { + float x, y, z; + float u, v; // 텍스처 u 및 v 좌표 추가 + D3DCOLOR color; +}; +// 정점 선언에서 텍스처 좌표를 추가해야 합니다. +// 텍스처의 왼쪽 상단은 u: 0, v: 0입니다. +std::vector vertices { + VStruct{ -1.0f, -1.0f, 1.0f, 0.0f, 1.0f, ... }, // 왼쪽 하단 + VStruct{ -1.0f, 1.0f, 1.0f, 0.0f, 0.0f, ... }, // 왼쪽 상단 + VStruct{ 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, ... }, // 오른쪽 상단 + VStruct{ 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, ... } // 오른쪽 하단 +}; +// 다음은 정점 선언입니다. +std::vector vertexDecl{ + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + // 텍스처 좌표에 사용되는 2D float 벡터를 추가합니다. + {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + // 색상 오프셋은 (3 + 2) * sizeof(float) = 20바이트가 아닙니다. + {0, 20, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, + D3DDECL_END() +}; +// 이제 텍스처를 로드하고 셰이더에 전달해야 합니다. +// ... +_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); +// png 파일에서 Direct3D 텍스처를 생성합니다. +result = D3DXCreateTextureFromFile(_device.Get(), // direct3d 장치 + "texture.png", // 텍스처 경로 + &_texture); // 수신 텍스처 포인터 +if (FAILED(result)) + return -1; +// 텍스처를 셰이더 스테이지 0에 연결합니다. 이는 픽셀 셰이더의 텍스처 레지스터 0과 동일합니다. +_device->SetTexture(0, _texture.Get()); +``` + +주요 코드가 준비되었으므로 이제 셰이더를 이러한 변경 사항에 맞게 조정해야 합니다.
+ +**정점 셰이더** + +```cpp +float4x4 projectionMatrix; +float4x4 viewMatrix; +float4x4 worldMatrix; +// 정점 셰이더 입력 및 출력에 텍스처 좌표를 추가합니다. +struct VS_INPUT { + float3 position : POSITION; + float2 texcoord : TEXCOORD; + float4 color : COLOR; +}; + +struct VS_OUTPUT { + float4 position : POSITION; + float2 texcoord : TEXCOORD; + float4 color : COLOR; +}; + +VS_OUTPUT main(VS_INPUT input) { + VS_OUTPUT output; + + output.position = float4(input.position, 1.0f); + output.position = mul(output.position, worldMatrix); + output.position = mul(output.position, viewMatrix); + output.position = mul(output.position, projectionMatrix); + + output.color = input.color; + // texcoord 출력을 입력으로 설정합니다. + output.texcoord = input.texcoord; + + return output; +} +``` + +**픽셀 셰이더** + +```cpp +// "sam0"라는 샘플러를 샘플러 레지스터 0을 사용하여 생성합니다. 이는 텍스처 스테이지 0과 동일하며, 텍스처를 전달했습니다. +sampler sam0 : register(s0); + +struct PS_INPUT { + float4 position : POSITION; + float2 texcoord : TEXCOORD; + float4 color : COLOR; +}; + +float4 main(PS_INPUT input) : COLOR{ + // 텍스처 색상과 입력 색상 사이를 선형 보간하고 + // 입력 색상의 75%를 사용합니다. + // tex2D는 지정된 텍스처 좌표에서 텍스처 데이터를 반환합니다. + return lerp(tex2D(sam0, input.texcoord), input.color, 0.75f); +} +``` + +## 인용 +[1][DirectX - 위키백과](https://en.wikipedia.org/wiki/DirectX) \ No newline at end of file diff --git a/ko/docker.md b/ko/docker.md new file mode 100644 index 0000000000..17fb177c9c --- /dev/null +++ b/ko/docker.md @@ -0,0 +1,228 @@ +--- +category: tool +name: Docker +filename: docker.bat +contributors: + - ["Ruslan López", "http://javapro.org/"] + - ["Michael Chen", "https://github.com/ML-Chen"] + - ["Akshita Dixit", "https://github.com/akshitadixit"] + - ["Marcel Ribeiro-Dantas", "https://github.com/mribeirodantas"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Docker는 다양한 머신에서 애플리케이션을 원활하게 빌드, 테스트, 배포 및 실행하는 데 도움이 되는 도구입니다. 소프트웨어에 필요한 환경을 모든 머신에서 복제합니다. [docs.docker.com/get-docker/](https://docs.docker.com/get-docker/)에서 Docker를 다운로드할 수 있습니다. + +지난 10년 동안 부피가 크고 느린 가상 머신에 비해 가볍고 빠르기 때문에 인기가 높아졌습니다. VM과 달리 docker는 시작하기 위해 자체적으로 완전한 OS를 로드할 필요가 없으며 실행 중인 애플리케이션이 사용할 리소스 외에는 리소스를 놓고 경쟁하지 않습니다. 반면에 VM은 프로세서, 디스크 및 메모리에 대한 리소스 집약적이므로 제한된 용량 아키텍처에서 다양한 애플리케이션에 대해 여러 VM을 실행하는 것이 어려워집니다. + +

+┌────────────────────────┐ ┌───────────────────────┐
+│      ┌───────────┐     │ │      ┌───────────┐    │
+│      │   App     │     │ │      │   App     │    │
+│      └───────────┘     │ │      └───────────┘    │
+│  ┌────────┐ ┌────────┐ │ │  ┌────────┐ ┌───────┐ │
+│  │  Libs  │ │  Deps  │ │ │  │  Libs  │ │  Deps │ │
+│  └────────┘ └────────┘ │ │  └────────┘ └───────┘ │
+│  ┌───────────────────┐ │ │  ┌──────────────────┐ │
+│  │      Guest OS     │ │ │  │     Guest OS     │ │
+│  └───────────────────┘ │ │  └──────────────────┘ │
+│           VM1          │ │           VM2         │
+└────────────────────────┘ └───────────────────────┘
+┌──────────────────────────────────────────────────┐
+│                     Hypervisor                   │
+└──────────────────────────────────────────────────┘
+┌──────────────────────────────────────────────────┐
+│                      Host OS                     │
+└──────────────────────────────────────────────────┘
+┌──────────────────────────────────────────────────┐
+│             Hardware Infrastructure              │
+└──────────────────────────────────────────────────┘
+              (VM 기반 아키텍처)
+
+┌────────────────────────┐ ┌───────────────────────┐
+│      ┌───────────┐     │ │      ┌───────────┐    │
+│      │   App     │     │ │      │   App     │    │
+│      └───────────┘     │ │      └───────────┘    │
+│  ┌────────┐ ┌────────┐ │ │  ┌────────┐ ┌───────┐ │
+│  │  Libs  │ │  Deps  │ │ │  │  Libs  │ │  Deps │ │
+│  └────────┘ └────────┘ │ │  └────────┘ └───────┘ │
+│        Container1      │ │       Container2      │
+└────────────────────────┘ └───────────────────────┘
+┌──────────────────────────────────────────────────┐
+│                       Docker                     │
+└──────────────────────────────────────────────────┘
+┌──────────────────────────────────────────────────┐
+│                        OS                        │
+└──────────────────────────────────────────────────┘
+┌──────────────────────────────────────────────────┐
+│             Hardware Infrastructure              │
+└──────────────────────────────────────────────────┘
+            (Docker 기반 아키텍처)
+
+ +자주 접하게 될 몇 가지 용어는 Docker 이미지와 Docker 컨테이너입니다. 이미지는 [Docker Hub](https://hub.docker.com/)와 같은 컨테이너 레지스트리에 모두 저장된 컨테이너의 패키지 또는 템플릿입니다. 컨테이너는 코드, 런타임, 시스템 도구, 시스템 라이브러리 및 설정 등 소프트웨어를 시작하고 실행하는 데 필요한 모든 것을 포함하는 이러한 이미지의 독립 실행형 실행 가능 인스턴스입니다. Docker는 CLI 클라이언트가 서버 구성 요소인 Docker Engine과 RESTful API를 사용하여 통신하여 명령을 실행하는 클라이언트-서버 아키텍처를 따릅니다. + +## Docker CLI + +```bash +# https://docs.docker.com/get-docker/에서 Docker를 설치한 후 +# 사용 가능한 명령을 나열하려면 매개변수 없이 `docker`를 실행하거나 +# `docker help`를 실행하십시오. +$ docker + +>>> docker [OPTIONS] COMMAND [ARG...] + docker [ --help | -v | --version ] + + 컨테이너를 위한 자급자족 런타임입니다. + + 옵션: + --config string 클라이언트 구성 파일 위치 (기본값 "/root/.docker") + -c, --context string 데몬에 연결하는 데 사용할 컨텍스트 이름 (DOCKER_HOST env var 및 "docker context use"로 설정된 기본 컨텍스트 재정의) + -D, --debug 디버그 모드 활성화 + --help 사용법 인쇄 + -H, --host value 연결할 데몬 소켓 (기본값 []) + -l, --log-level string 로깅 수준 설정 ("debug"|"info"|"warn"|"error"|"fatal") (기본값 "info") + --tls TLS 사용; --tlsverify에 의해 암시됨 + --tlscacert string 이 CA에서 서명한 인증서만 신뢰 (기본값 "/root/.docker/ca.pem") + --tlscert string TLS 인증서 파일 경로 (기본값 "/root/.docker/cert.pem") + --tlskey string TLS 키 파일 경로 (기본값 "/root/.docker/key.pem") + --tlsverify TLS 사용 및 원격 확인 + -v, --version 버전 정보 인쇄 및 종료 + + 명령: + attach 실행 중인 컨테이너에 연결 + # [...] + +$ docker run hello-world +# `docker run `은 컨테이너를 실행하는 데 사용되며, 시스템에 이미 존재하지 않는 경우 Docker Hub에서 이미지를 가져옵니다. 여기서 docker 클라이언트는 데몬에 연결하고, 데몬은 Docker Hub에서 "hello-world" 이미지를 가져옵니다. 그런 다음 데몬은 이미지에서 새 컨테이너를 빌드하고, 이 컨테이너는 터미널에서 볼 수 있는 클라이언트로 스트리밍되는 출력을 생성하는 실행 파일을 실행합니다. + +$ docker run -d ubuntu sleep 60s +# -d (또는 --detach) 플래그는 컨테이너를 백그라운드에서 실행하고 터미널로 돌아가고 싶을 때 사용합니다. 여기서 우리는 ubuntu 컨테이너를 터미널에서 분리하고, 출력은 id여야 하며 명령이 종료됩니다. 실행 중인 컨테이너를 확인하면 여전히 우리 컨테이너가 있어야 합니다: +# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +# 133261b4894a ubuntu "sleep 60s" 3 seconds ago Up 2 seconds vigorous_gould + +$ docker run -p 3000:8000 +# -p (또는 --publish) 플래그는 컨테이너 내부의 포트 8000을 컨테이너 외부의 포트 3000에 노출하는 데 사용됩니다. 이는 컨테이너 내부의 앱이 격리되어 실행되므로 앱이 실행되는 포트 8000이 컨테이너에 비공개이기 때문입니다. + +$ docker run -i +# 또는 +$ docker run -it +# Docker는 컨테이너를 비대화형 모드로 실행합니다. 즉, 실행 중에 입력을 받거나 동적으로 작동하지 않습니다. -i 플래그는 컨테이너에 대한 입력을 열어두고, -t 플래그는 셸이 연결할 수 있는 의사 터미널을 생성합니다(-it로 결합 가능). + +$ docker ps -a +# `docker ps` 명령은 기본적으로 실행 중인 컨테이너만 표시합니다. 모든 컨테이너를 보려면 -a (또는 --all) 플래그를 사용하십시오. +# 위 명령을 실행하면 터미널에 다음과 유사한 내용이 출력되어야 합니다: +# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +# 82f84bf6912b hello-world "/hello" 9 minutes ago Exited (0) 9 minutes ago eloquent_sammet + + +$ docker stop hello-world +# 또는 +$ docker start hello-world +# stop 명령은 하나 이상의 컨테이너를 중지하고, start 명령은 컨테이너를 다시 시작합니다! `docker start -a ubuntu`는 분리된 컨테이너를 터미널에 다시 연결합니다. 즉, 전경에서 실행됩니다. + +$ docker create alpine +# `docker create`는 지정된 이미지(여기서는 alpine)로 새 컨테이너를 생성하며, 컨테이너는 `docker run`과 달리 자동 시작되지 않습니다. 이 명령은 컨테이너 구성을 설정한 다음 필요할 때 `docker start`를 사용하여 시작하는 데 사용됩니다. 상태가 "Created"인지 확인하십시오: +# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +# 4c71c727c73d alpine "/bin/sh" 29 seconds ago Created naughty_ritchie + +$ docker rm 82f84 +# 컨테이너 ID를 사용하여 하나 이상의 컨테이너를 제거합니다. +# P.S.: 전체 ID의 처음 몇 자만 사용하여 컨테이너를 식별할 수 있습니다. + +$ docker images +# 모든 이미지와 해당 정보를 표시합니다. 여기서 created는 Docker Hub에서 업데이트된 최신 이미지 태그를 의미합니다: +# REPOSITORY TAG IMAGE ID CREATED SIZE +# ubuntu latest a8780b506fa4 9 days ago 77.8MB +# alpine latest 9c6f07244728 3 months ago 5.54MB +# hello-world latest feb5d9fea6a5 13 months ago 13.3kB + +$ docker rmi +# 인스턴스(또는 우리가 아는 컨테이너)가 실행 중이 아닌 시스템에서 하나 이상의 이미지를 제거합니다. 이미지에 연결된 컨테이너가 있는 경우 먼저 컨테이너를 삭제하거나 -f (또는 --force) 플래그를 사용하여 컨테이너와 이미지를 모두 강제로 삭제하십시오. + +$ docker pull busybox +# pull 명령은 지정된 이미지를 Docker Hub에서 시스템으로 다운로드합니다. + +$ docker exec -it 7b272 bash +# 이 명령은 실행 중인 컨테이너의 기본 디렉토리에서 명령을 실행하는 데 사용됩니다. 여기서 7b272는 ubuntu 컨테이너였으며 위 명령은 bash 세션을 열어 컨테이너와 상호 작용하는 데 도움이 됩니다. + +$ docker logs +# 지정된 컨테이너에서 기록된 정보를 표시합니다. +# root@7b27222e4bb7:/# whoami +# root +# root@7b27222e4bb7:/# pwd +# / +# root@7b27222e4bb7:/# ls +# bin boot dev etc home lib lib32 lib64 libx3 srv sys tmp usr var +# root@7b27222e4bb7:/# exit +# exit + +# 더 많은 명령은 https://docs.docker.com/engine/reference/commandline/docker/에서 찾을 수 있습니다. +``` + +## Dockerfile +Dockerfile은 Docker 이미지의 청사진입니다. 애플리케이션의 아티팩트와 해당 구성을 이 파일에 특정 구문으로 언급하여 누구나 애플리케이션의 Docker 이미지를 만들 수 있도록 할 수 있습니다. + +### 몇 가지 유의 사항: + +* 항상 확장자 없이 `Dockerfile`이라는 이름으로 엄격하게 지정됩니다. +* 이미 사용 가능한 일부 Docker 기본 이미지 위에 사용자 지정 이미지를 빌드해야 합니다. (`scratch`라는 빈 이미지가 있어 말 그대로 처음부터 이미지를 빌드할 수 있습니다.) +* 대문자로 된 모든 명령은 구문의 일부이며 대소문자를 구분하지 않지만 규칙처럼 사용됩니다. +* 아래는 샘플 Dockerfile이지만 [공식 문서](https://docs.docker.com/engine/reference/builder/)에서 자세히 읽을 수 있습니다. + +```dockerfile +FROM +# 기본 이미지 정의 + +ENV USERNAME='admin'\ + PWD='****' +# 선택적으로 환경 변수 정의 + +RUN apt-get update +# 컨테이너 환경 내에서 linux 명령 실행, 호스트 환경에 영향을 주지 않음 +# 이미지 생성 시 실행됩니다. + +COPY +# 호스트에서 실행, src(보통 호스트에 있음)에서 컨테이너의 target으로 파일 복사 + +ENTRYPOINT ["some-script.sh"] +# 전체 스크립트를 진입점으로 실행 + +CMD [...] +# 항상 dockerfile의 일부, 진입점 linux 명령 도입 예: +# `CMD node server.js` +# 이미지 생성 후 이미지의 컨테이너가 실행 중일 때만 실행됩니다. +``` + +### 이미지 빌드 +애플리케이션을 Docker 이미지로 래핑한 후 `docker build` 명령을 사용하여 실행(또는 빌드)합니다. + +```bash +$ docker build +# 지정된 Dockerfile에서 이미지를 빌드하는 데 사용됩니다. +# 경로 대신 URL을 지정할 수도 있습니다. +# -t 태그는 선택 사항이며 이미지에 이름과 태그를 지정하는 데 사용됩니다. 예: +# `$ docker build -t my-image:0.1 ./home/app` +# dockerfile을 변경할 때마다 이미지를 다시 빌드하십시오. +``` + +## DockerHub에 이미지 푸시 +애플리케이션의 Docker 이미지를 모든 Docker 사용자가 공개적으로 사용할 수 있도록 하려면 [Docker Hub](https://hub.docker.com/)에 푸시해야 합니다. Docker Hub는 Docker 이미지의 레지스트리입니다. Docker Hub에 사용자 이름과 암호가 있는 계정이 있는지 확인하십시오. + +Docker Hub에 이미지를 푸시할 때 소스 이미지 이름의 일부로 Docker Hub 사용자 이름을 지정해야 합니다. GitHub 리포지토리와 마찬가지로 사용자 이름/이미지 이름의 태그 이름으로 대상 이미지를 만들어야 합니다. + +```bash +$ docker login +# 사용자 이름과 암호를 사용하여 Docker Hub에 로그인 + +$ docker tag [:] [:] +# 로컬 src-image를 공용 target-image에 태그 지정 +# 예: `docker tag my-sample-app:1.0.0 akshitadixit/my-sample-app` +# 태그가 지정되지 않은 경우 기본값은 `latest`입니다. + +$ docker push [:] +# 이미지를 Docker Hub에 업로드 +# 예: `docker push akshitadixit/my-sample-app` +# 이 이미지는 프로필의 리포지토리에서 +# `https://hub.docker.com/r/username/image-name`으로 액세스할 수 있습니다. +``` \ No newline at end of file diff --git a/ko/dynamic-programming.md b/ko/dynamic-programming.md new file mode 100644 index 0000000000..fab8ba798d --- /dev/null +++ b/ko/dynamic-programming.md @@ -0,0 +1,63 @@ +--- +category: Algorithms & Data Structures +name: Dynamic Programming +contributors: + - ["Akashdeep Goel", "http://github.com/akashdeepgoel"] + - ["Miltiadis Stouras", "https://github.com/mstou"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +# 동적 프로그래밍 + +## 소개 + +동적 프로그래밍은 우리가 보게 될 특정 종류의 문제를 해결하는 데 사용되는 강력한 기술입니다. 아이디어는 매우 간단합니다. 주어진 입력으로 문제를 해결했다면 나중에 참조할 수 있도록 결과를 저장하여 동일한 문제를 다시 해결하는 것을 피하십시오. + +항상 기억하십시오! +"과거를 기억하지 못하는 자는 그것을 반복할 수밖에 없다" + +## 이러한 문제를 해결하는 방법 + +1. *하향식* : 주어진 문제를 분해하여 해결하기 시작합니다. 문제가 이미 해결된 것을 보면 저장된 답을 반환하기만 하면 됩니다. 해결되지 않은 경우 해결하고 답을 저장하십시오. 이것은 일반적으로 생각하기 쉽고 매우 직관적입니다. 이것을 메모이제이션이라고 합니다. + +2. *상향식* : 문제를 분석하고 하위 문제가 해결되는 순서를 확인하고 사소한 하위 문제부터 주어진 문제까지 해결하기 시작합니다. 이 과정에서 문제를 해결하기 전에 하위 문제가 해결되는 것이 보장됩니다. 이것을 동적 프로그래밍이라고 합니다. + +## 동적 프로그래밍의 예 + +최장 증가 부분 수열 문제는 주어진 수열의 가장 긴 증가 부분 수열을 찾는 것입니다. 수열 `S={ a1, a2, a3, a4, ............., an-1, an }`이 주어지면, 부분 집합에서 모든 `j`와 `i`에 대해 `j a[j] and LS[i] 0 + # h는 함수에서 처음 사용되므로 + # 지역 변수입니다. + h = b + b = a mod b + a = h + . + res = a +. +call gcd 120 35 r +print r +# +# 문자열을 연결할 수 있으며 숫자는 +# 자동으로 문자열로 변환됩니다. +# +print "1 + 2 = " & 1 + 2 +# +# 숫자 배열 +# +a[] = [ 2.1 3.14 3 ] +# +# 배열은 커질 수 있습니다. +a[] &= 4 +print a[] +# +# 배열, 문자열 및 숫자는 값으로 복사됩니다. +# +b[] = a[] +a[] &= 4 +print a[] ; print b[] +# +# 배열 교환은 빠릅니다. +# +swap a[] b[] +print a[] ; print b[] +# +# 문자열 배열 +# +fruits$[] = [ "apple" "banana" "orange" ] +# +# for-in은 배열의 요소를 반복합니다. +# +for fruit$ in fruits$[] + print fruit$ +. +# +# 문자열은 단일 문자에도 사용됩니다. +# +letters$[] = str_chars "ping" +print letters$[] +letters$[1] = "o" +print str_join letters$[] +# +# 2차원 배열은 배열의 배열입니다. +# 이것은 길이 4의 3개 배열을 정의합니다. +# +len a[][] 3 +for i range len a[][] + len a[i][] 4 +. +a[1][2] = 99 +print a[][] +# +# 내장 함수 +if sin 90 = 1 + print "angles are in degree" +. +print pow 2 8 +# 1970년 이후 초 +print floor sys_time +# 난수 +print randomf +print random 6 + 1 +# +# 시와 분 +print substr time_str sys_time 11 5 +# +print str_ord "A" +print str_chr 65 +# +# 숫자 형식 설정 +numfmt 0 4 +print sqrt 2 +print pi +print logn 10 +# +a$[] = str_split "10,15,22" "," +print a$[] +print 2 * number a$[0] +print len a$[] +print len "Hello" +# +# 'break n'을 사용하면 중첩된 루프와 함수를 나갈 수 있습니다. +# +names$[] = [ ] +func name2id name$ . id . + for id range len names$[] + if names$[id] = name$ + # 루프와 함수를 나갑니다. + break 2 + . + . + names$[] &= name$ +. +call name2id "alice" id ; print id +call name2id "bob" id ; print id +call name2id "alice" id ; print i +# +# 'repeat'를 사용하면 루프를 만들 수 있으며, 'until'을 사용하여 +# 루프 본문에서 나갈 수 있습니다. +# +sum = 0 +repeat + s$ = input + until s$ = "" + sum += number s$ +. +print "sum: " & sum +# +# "input"은 "input_data" 섹션에서 문자열을 읽습니다. +# 있는 경우, 그렇지 않으면 프롬프트를 통해 읽습니다. +# +input_data +10 +-2 +6 +``` + +내장 그래픽 기본 요소 및 이벤트 기반 프로그래밍 + +``` +# 마우스를 사용한 간단한 그리기 +# +set_linewidth 4 +set_color 900 +# 색상은 0에서 999까지 코딩되며, +# 왼쪽 숫자는 빨간색 구성 요소를 지정하고, +# 가운데 숫자는 녹색 구성 요소를 지정하고, +# 오른쪽 숫자는 파란색 구성 요소를 지정합니다. +# +on mouse_down + down = 1 + move_pen mouse_x mouse_y + # 그리기 펜을 실제 마우스 위치로 이동합니다. + draw_circle 2 +. +on mouse_up + down = 0 +. +on mouse_move + if down = 1 + draw_line mouse_x mouse_y + . +. +``` + +``` +# 애니메이션 진자 +# +on animate + # 애니메이션 이벤트는 각 화면 새로 고침 후에 발생합니다. + # + clear_screen + move_pen 50 50 + draw_circle 1 + x = 50 + 40 * sin ang + y = 50 - 40 * cos ang + draw_line x y + draw_circle 5 + vel += sin ang / 5 + ang += vel +. +ang = 10 +``` + +* [Easylang에 대한 추가 정보](https://easylang.online/) + +* [소스 코드](https://github.com/chkas/easylang) \ No newline at end of file diff --git a/ko/edn.md b/ko/edn.md new file mode 100644 index 0000000000..8141094e8d --- /dev/null +++ b/ko/edn.md @@ -0,0 +1,107 @@ +--- +name: EDN +filename: learnedn.edn +contributors: + - ["Jason Yeo", "https://github.com/jsyeo"] + - ["Jonathan D Johnston", "https://github.com/jdjohnston"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +확장 가능한 데이터 표기법(EDN)은 데이터를 직렬화하기 위한 형식입니다. + +EDN은 Clojure에서 사용되는 구문의 하위 집합입니다. EDN으로 정의된 데이터를 읽는 것은 특히 신뢰할 수 없는 소스에서 전체 Clojure 구문으로 정의된 데이터를 읽는 것보다 안전합니다. EDN은 데이터로 제한되며 코드는 없습니다. JSON과 의도가 비슷합니다. Clojure에서 더 일반적으로 사용되지만 다른 많은 언어에 대한 EDN 구현이 있습니다. + +JSON 및 YAML에 대한 EDN의 주요 이점은 확장 가능하다는 것입니다. 나중에 확장되는 방법을 살펴보겠습니다. + +```clojure +; 주석은 세미콜론으로 시작합니다. +; 세미콜론 뒤의 모든 것은 무시됩니다. + +;;;;;;;;;;;;;;;;;;; +;;; 기본 유형 ;;; +;;;;;;;;;;;;;;;;;;; + +il ; 다른 언어에서는 null로도 알려져 있습니다. + +; 부울 +true +false + +; 문자열은 큰따옴표로 묶습니다. +"hungarian breakfast" +"farmer's cheesy omelette" + +; 문자는 백슬래시가 앞에 옵니다. +\g \r \a \c \e + +; 키워드는 콜론으로 시작합니다. 열거형처럼 작동합니다. Ruby의 심볼과 비슷합니다. +:eggs +:cheese +:olives + +; 심볼은 식별자를 나타내는 데 사용됩니다. +; /를 사용하여 심볼에 네임스페이스를 지정할 수 있습니다. / 앞의 모든 것은 심볼의 네임스페이스입니다. +spoon +kitchen/spoon ; spoon과 다릅니다. +kitchen/fork +github/fork ; 이것으로 먹을 수 없습니다. + +; 정수 및 부동 소수점 +42 +3.14159 + +; 목록은 값의 시퀀스입니다. +(:bun :beef-patty 9 "yum!") + +; 벡터는 임의 접근을 허용합니다. +[:gelato 1 2 -2] + +; 맵은 키를 값과 연결하는 연관 데이터 구조입니다. +{:eggs 2 + :lemon-juice 3.5 + :butter 1} + +; 키로 키워드를 사용하는 데 제한이 없습니다. +{[1 2 3 4] "tell the people what she wore", + [5 6 7 8] "the more you see the more you hate"} + +; 가독성을 위해 쉼표를 사용할 수 있습니다. 공백으로 처리됩니다. + +; 집합은 고유한 요소를 포함하는 컬렉션입니다. +#{:a :b 88 "huat"} + +;;;;;;;;;;;;;;;;;;;;;;; +;;; 태그가 지정된 요소 ;;; +;;;;;;;;;;;;;;;;;;;;;;; + +; EDN은 # 기호로 요소를 태그 지정하여 확장할 수 있습니다. + +#MyYelpClone/MenuItem {:name "eggs-benedict" :rating 10} + +; Clojure 예제로 설명하겠습니다. 해당 EDN 조각을 MenuItem 레코드로 변환하고 싶다고 가정합니다. + +(defrecord MenuItem [name rating]) + +; defrecord는 무엇보다도 map->MenuItem을 정의했습니다. 이 함수는 필드 이름(키워드)에서 값으로의 맵을 가져와 user.MenuItem 레코드를 생성합니다. + +; EDN을 Clojure 값으로 변환하려면 내장 EDN 리더인 clojure.edn/read-string을 사용해야 합니다. + +(clojure.edn/read-string "{:eggs 2 :butter 1 :flour 5}") +; -> {:eggs 2 :butter 1 :flour 5} + +; 태그가 지정된 요소를 변환하려면 clojure.edn/read-string에 태그 심볼을 데이터 리더 함수에 매핑하는 :readers 맵이 있는 옵션 맵을 전달하십시오. + +(clojure.edn/read-string + {:readers {'MyYelpClone/MenuItem map->MenuItem}} + "#MyYelpClone/MenuItem {:name \"eggs-benedict\" :rating 10}") +; -> #user.MenuItem{:name "eggs-benedict", :rating 10} +``` + +# 참조 + +- [EDN 사양](https://github.com/edn-format/edn) +- [구현](https://github.com/edn-format/edn/wiki/Implementations) +- [태그가 지정된 요소](http://www.compoundtheory.com/clojure-edn-walkthrough/) + +``` \ No newline at end of file diff --git a/ko/elisp.md b/ko/elisp.md new file mode 100644 index 0000000000..408ef820ac --- /dev/null +++ b/ko/elisp.md @@ -0,0 +1,343 @@ +--- +name: Emacs Lisp +contributors: + - ["Bastien Guerry", "https://bzg.fr"] + - ["Saurabh Sandav", "http://github.com/SaurabhSandav"] + - ["rilysh", "https://github.com/rilysh"] +filename: learn-emacs-lisp.el +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```elisp +;; 이 문서는 15분 만에 Emacs Lisp에 대한 소개를 제공합니다 (v0.2d) +;; +;; 먼저 Peter Norvig의 이 글을 읽으십시오: +;; http://norvig.com/21-days.html +;; +;; 그런 다음 최신 버전의 GNU Emacs를 설치하십시오: +;; +;; Debian: apt-get install emacs (또는 배포판 지침 참조) +;; OSX: https://emacsformacosx.com/ +;; Windows: https://ftp.gnu.org/gnu/emacs/windows/ +;; +;; 더 일반적인 정보는 다음에서 찾을 수 있습니다: +;; http://www.gnu.org/software/emacs/#Obtaining + +;; 중요한 경고: +;; +;; 이 튜토리얼을 진행해도 컴퓨터가 손상되지 않습니다. +;; 화가 나서 바닥에 던지지 않는 한 말이죠. 그 경우, +;; 저는 어떠한 책임도 지지 않습니다. 즐거운 시간 보내세요! + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; Emacs를 실행하십시오. +;; +;; 환영 메시지를 닫으려면 `q` 키를 누르십시오. +;; +;; 이제 창 하단의 회색 줄을 보십시오: +;; +;; "*scratch*"는 현재 있는 편집 공간의 이름입니다. +;; 이 편집 공간을 "버퍼"라고 합니다. +;; +;; 스크래치 버퍼는 Emacs를 열 때 기본 버퍼입니다. +;; 파일을 편집하는 것이 아니라, 파일에 저장할 수 있는 버퍼를 편집하는 것입니다. +;; +;; "Lisp interaction"은 여기에서 사용할 수 있는 명령 집합을 나타냅니다. +;; +;; Emacs에는 모든 버퍼에서 사용할 수 있는 내장 명령 집합이 있으며, +;; 특정 모드를 활성화할 때 사용할 수 있는 여러 하위 집합의 명령이 있습니다. +;; 여기서는 Elisp 코드를 평가하고 탐색하는 명령이 포함된 `lisp-interaction-mode`를 사용합니다. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; 세미콜론은 줄의 어느 곳에서나 주석을 시작합니다. +;; +;; Elisp 프로그램은 기호 표현식("sexps")으로 구성됩니다: +(+ 2 2) + +;; 이 기호 표현식은 "2에 2를 더하라"고 읽습니다. + +;; Sexps는 괄호로 묶여 있으며, 중첩될 수 있습니다: +(+ 2 (+ 1 1)) + +;; 기호 표현식에는 원자 또는 다른 기호 표현식이 포함됩니다. +;; 위 예제에서 1과 2는 원자이고, +;; (+ 2 (+ 1 1))과 (+ 1 1)은 기호 표현식입니다. + +;; `lisp-interaction-mode`에서 sexps를 평가할 수 있습니다. +;; 닫는 괄호 바로 뒤에 커서를 놓고 +;; 컨트롤을 누른 상태에서 j 키를 누르십시오(줄여서 "C-j"). + +(+ 3 (+ 1 2)) +;; ^ 커서 여기 +;; `C-j` => 6 + +;; `C-j`는 평가 결과를 버퍼에 삽입합니다. + +;; `C-xC-e`는 Emacs 하단 줄에 동일한 결과를 표시합니다. +;; 이를 "에코 영역"이라고 합니다. 우리는 일반적으로 `C-xC-e`를 사용할 것입니다. +;; 버퍼를 불필요한 텍스트로 어지럽히고 싶지 않기 때문입니다. + +;; `setq`는 변수에 값을 저장합니다: +(setq my-name "Bastien") +;; `C-xC-e` => "Bastien" (에코 영역에 표시됨) + +;; `insert`는 커서가 있는 곳에 "Hello!"를 삽입합니다: +(insert "Hello!") +;; `C-xC-e` => "Hello!" + +;; 우리는 "Hello!"라는 단일 인수로 `insert`를 사용했지만, +;; 더 많은 인수를 전달할 수 있습니다 -- 여기서는 두 개를 사용합니다: + +(insert "Hello" " world!") +;; `C-xC-e` => "Hello world!" + +;; 문자열 대신 변수를 사용할 수 있습니다: +(insert "Hello, I am " my-name) +;; `C-xC-e` => "Hello, I am Bastien" + +;; sexps를 함수로 결합할 수 있습니다: +(defun hello () (insert "Hello, I am " my-name)) +;; `C-xC-e` => hello + +;; 함수를 평가할 수 있습니다: +(hello) +;; `C-xC-e` => Hello, I am Bastien + +;; 함수의 정의에 있는 빈 괄호는 +;; 인수를 받지 않음을 의미합니다. 하지만 항상 `my-name`을 사용하는 것은 +;; 지루하므로, 함수가 하나의 인수를 받도록 알려줍시다(여기서 +;; 인수는 "name"이라고 합니다): + +(defun hello (name) (insert "Hello " name)) +;; `C-xC-e` => hello + +;; 이제 고유한 인수에 대한 값으로 문자열 "you"를 사용하여 함수를 호출해 보겠습니다: +(hello "you") +;; `C-xC-e` => "Hello you" + +;; 네! + +;; 숨을 고르십시오. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; 이제 다른 창에서 "*test*"라는 새 버퍼로 전환하십시오: + +(switch-to-buffer-other-window "*test*") +;; `C-xC-e` +;; => [화면에 두 개의 창이 있고 커서는 *test* 버퍼에 있습니다] + +;; 마우스를 위쪽 창 위로 가져가 왼쪽 클릭하여 돌아갑니다. 또는 `C-xo`를 사용할 수 있습니다. +;; (즉, 컨트롤-x를 누른 상태에서 o를 누름)를 사용하여 대화식으로 다른 창으로 이동합니다. + +;; `progn`을 사용하여 여러 sexps를 결합할 수 있습니다: +(progn + (switch-to-buffer-other-window "*test*") + (hello "you")) +;; `C-xC-e` +;; => [화면에 두 개의 창이 있고 커서는 *test* 버퍼에 있습니다] + +;; 이제 괜찮으시다면, `C-xC-e`를 누르라고 요청하는 것을 멈추겠습니다: 다음 모든 sexp에 대해 그렇게 하십시오. + +;; 항상 마우스 또는 `C-xo`를 사용하여 *scratch* 버퍼로 돌아갑니다. + +;; 버퍼를 지우는 것이 종종 유용합니다: +(progn + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (hello "there")) + +;; 또는 다른 창으로 돌아가기: +(progn + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (hello "you") + (other-window 1)) + +;; `let`을 사용하여 지역 변수에 값을 바인딩할 수 있습니다: +(let ((local-name "you")) + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (hello local-name) + (other-window 1)) + +;; 이 경우 `progn`을 사용할 필요가 없습니다. `let`도 여러 sexps를 결합하기 때문입니다. + +;; 문자열 서식 지정: +(format "Hello %s!\n" "visitor") + +;; %s는 "visitor"로 대체되는 문자열의 자리 표시자입니다. +;; \n은 줄 바꿈 문자입니다. + +;; format을 사용하여 함수를 구체화해 보겠습니다: +(defun hello (name) + (insert (format "Hello %s!\n" name))) + +(hello "you") + +;; `let`을 사용하는 다른 함수를 만들어 보겠습니다: +(defun greeting (name) + (let ((your-name "Bastien")) + (insert (format "Hello %s!\n\nI am %s." + name ; 함수의 인수 + your-name ; let으로 바인딩된 변수 "Bastien" + )))) + +;; 그리고 평가합니다: +(greeting "you") + +;; 일부 함수는 대화식입니다: +(read-from-minibuffer "Enter your name: ") + +;; 이 함수를 평가하면 프롬프트에 입력한 내용이 반환됩니다. + +;; `greeting` 함수가 이름을 묻도록 만들어 보겠습니다: +(defun greeting (from-name) + (let ((your-name (read-from-minibuffer "Enter your name: "))) + (insert (format "Hello!\n\nI am %s and you are %s." + from-name ; 함수의 인수 + your-name ; let으로 바인딩된 변수, 프롬프트에서 입력됨 + )))) + +(greeting "Bastien") + +;; 다른 창에 결과를 표시하여 완성해 보겠습니다: +(defun greeting (from-name) + (let ((your-name (read-from-minibuffer "Enter your name: "))) + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (insert (format "Hello %s!\n\nI am %s." your-name from-name)) + (other-window 1))) + +;; 이제 테스트합니다: +(greeting "Bastien") + +;; 숨을 고르십시오. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; 이름 목록 저장: +;; 데이터의 리터럴 목록을 만들려면 '를 사용하여 평가되지 않도록 하십시오 - 말 그대로 데이터를 "인용"하십시오. +(setq list-of-names '("Sarah" "Chloe" "Mathilde")) + +;; `car`를 사용하여 이 목록의 첫 번째 요소를 가져옵니다: +(car list-of-names) + +;; `cdr`을 사용하여 첫 번째 요소를 제외한 모든 요소의 목록을 가져옵니다: +(cdr list-of-names) + +;; `push`를 사용하여 목록의 시작 부분에 요소를 추가합니다: +(push "Stephanie" list-of-names) + +;; 참고: `car`와 `cdr`은 목록을 수정하지 않지만 `push`는 수정합니다. +;; 이것은 중요한 차이점입니다: 일부 함수는 부작용이 없지만(`car`와 같이) 다른 함수는 있습니다(`push`와 같이). + +;; `list-of-names`의 각 요소에 대해 `hello`를 호출해 보겠습니다: +(mapcar 'hello list-of-names) + +;; `list-of-names`의 모든 사람에게 인사하도록 `greeting`을 구체화합니다: +(defun greeting () + (switch-to-buffer-other-window "*test*") + (erase-buffer) + (mapcar 'hello list-of-names) + (other-window 1)) + +(greeting) + +;; 위에서 정의한 `hello` 함수를 기억하십니까? 이름이라는 하나의 인수를 받습니다. +;; `mapcar`는 `hello`를 호출하고, `list-of-names`의 각 요소를 `hello`의 인수로 순차적으로 사용합니다. + +;; 이제 표시된 버퍼에 있는 것을 약간 정리해 보겠습니다: + +(defun replace-hello-by-bonjour () + (switch-to-buffer-other-window "*test*") + (goto-char (point-min)) + (while (search-forward "Hello") + (replace-match "Bonjour")) + (other-window 1)) + +;; (goto-char (point-min))은 버퍼의 시작으로 이동합니다. +;; (search-forward "Hello")는 문자열 "Hello"를 검색합니다. +;; (while x y)는 x가 무언가를 반환하는 동안 y sexp를 평가합니다. +;; x가 `nil`(아무것도 없음)을 반환하면 while 루프를 종료합니다. + +(replace-hello-by-bonjour) + +;; *test* 버퍼에서 "Hello"의 모든 발생이 +;; "Bonjour"로 대체된 것을 볼 수 있습니다. + +;; 또한 "Search failed: Hello"라는 오류가 발생해야 합니다. +;; +;; 이 오류를 피하려면 `search-forward`에 버퍼의 특정 지점에서 검색을 중지해야 하는지 여부와 +;; 아무것도 찾지 못했을 때 자동으로 실패해야 하는지 여부를 알려야 합니다: + +;; (search-forward "Hello" nil t)가 그 역할을 합니다: + +;; `nil` 인수는 검색이 위치에 바인딩되지 않음을 의미합니다. +;; `'t'` 인수는 아무것도 찾지 못했을 때 자동으로 실패함을 의미합니다. + +;; 오류를 발생시키지 않는 아래 함수에서 이 sexp를 사용합니다: + +(defun hello-to-bonjour () + (switch-to-buffer-other-window "*test*") + (erase-buffer) + ;; `list-of-names`의 이름에 인사 + (mapcar 'hello list-of-names) + (goto-char (point-min)) + ;; "Hello"를 "Bonjour"로 바꾸기 + (while (search-forward "Hello" nil t) + (replace-match "Bonjour")) + (other-window 1)) + +(hello-to-bonjour) + +;; 이름을 굵게 만들어 보겠습니다: + +(defun boldify-names () + (switch-to-buffer-other-window "*test*") + (goto-char (point-min)) + (while (re-search-forward "Bonjour \(.+\)!") + (add-text-properties (match-beginning 1) + (match-end 1) + (list 'face 'bold))) + (other-window 1)) + +;; 이 함수는 `re-search-forward`를 도입합니다: 문자열 "Bonjour"를 검색하는 대신 +;; "정규 표현식"(접두사 "re-"로 축약됨)을 사용하여 패턴을 검색합니다. + +;; 정규 표현식은 "Bonjour \(.+\)!")이며 다음과 같이 읽습니다: +;; 문자열 "Bonjour "와 +;; 그룹 | 이것은 \( ... \) 구문입니다. +;; 모든 문자 | 이것은 .입니다. +;; 반복될 수 있음 | 이것은 +입니다. +;; 그리고 "!" 문자열. + +;; 준비되셨습니까? 테스트하십시오! + +(boldify-names) + +;; `add-text-properties`는 얼굴과 같은 텍스트 속성을 추가합니다. + +;; 좋습니다, 끝났습니다. 즐거운 해킹 되세요! + +;; 변수나 함수에 대해 더 알고 싶다면: +;; +;; C-h v a-variable RET +;; C-h f a-function RET +;; +;; Emacs로 Emacs Lisp 설명서를 읽으려면: +;; +;; C-h i m elisp RET +;; +;; Emacs Lisp에 대한 온라인 소개를 읽으려면: +;; https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html +``` + +### 추가 자료 +- [GNU Elisp 설명서](https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html) +- [Emacs 위키](https://www.emacswiki.org/emacs/LearningEmacs) +- [Emacs 문서](https://emacsdocs.org/docs/elisp/Emacs-Lisp) +- [Mpre Elisp 문서](https://www.math.utah.edu/docs/info/elisp_22.html) \ No newline at end of file diff --git a/ko/elixir.md b/ko/elixir.md new file mode 100644 index 0000000000..4e74e30e0f --- /dev/null +++ b/ko/elixir.md @@ -0,0 +1,437 @@ +--- +name: Elixir +contributors: + - ["Joao Marques", "https://github.com/mrshankly"] + - ["Dzianis Dashkevich", "https://github.com/dskecse"] + - ["Ryan Plant", "https://github.com/ryanplant-au"] + - ["Ev Bogdanov", "https://github.com/evbogdanov"] +filename: learnelixir.ex +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Elixir는 Erlang VM 위에 구축된 현대적인 함수형 언어입니다. +Erlang과 완벽하게 호환되지만, 더 표준적인 구문과 많은 추가 기능을 제공합니다. + +```elixir +# 한 줄 주석은 숫자 기호로 시작합니다. + +# 여러 줄 주석은 없지만, +# 여러 주석을 쌓을 수 있습니다. + +# Elixir 셸을 사용하려면 `iex` 명령을 사용하십시오. +# `elixirc` 명령으로 모듈을 컴파일하십시오. + +# Elixir를 올바르게 설치했다면 둘 다 경로에 있어야 합니다. + +## --------------------------- +## -- 기본 유형 +## --------------------------- + +# 숫자가 있습니다. +3 # 정수 +0x1F # 정수 +3.0 # 부동 소수점 + +# 원자는 값이 자신의 이름인 상수입니다. 콜론으로 시작합니다. +:hello # 원자 + +# 메모리에 연속적으로 저장되는 튜플입니다. +{1,2,3} # 튜플 + +# `elem` 함수로 튜플 요소에 액세스할 수 있습니다: +elem({1, 2, 3}, 0) #=> 1 + +# 연결 리스트로 구현된 리스트입니다. +[1,2,3] # 리스트 + +# 다음과 같이 리스트의 머리와 꼬리에 액세스할 수 있습니다: +[head | tail] = [1,2,3] +head #=> 1 +tail #=> [2,3] + +# Elixir에서는 Erlang과 마찬가지로 `=`는 할당이 아닌 패턴 매칭을 나타냅니다. +# +# 이는 왼쪽(패턴)이 오른쪽과 일치함을 의미합니다. +# +# 이것이 리스트의 머리와 꼬리에 액세스하는 위 예제가 작동하는 방식입니다. + +# 양쪽이 일치하지 않으면 패턴 매칭이 오류를 발생시킵니다. 이 예제에서 +# 튜플의 크기가 다릅니다. +# {a, b, c} = {1, 2} #=> ** (MatchError) 오른쪽 값과 일치하지 않음: {1,2} + +# 바이너리도 있습니다. +<<1,2,3>> # 바이너리 + +# 문자열 및 문자 리스트 +"hello" # 문자열 +'hello' # 문자 리스트 + +# 여러 줄 문자열 +""" +나는 여러 줄 +문자열입니다. +""" +#=> "나는 여러 줄\n문자열입니다.\n" + +# 문자열은 모두 UTF-8로 인코딩됩니다: +"héllò" #=> "héllò" + +# 문자열은 실제로 바이너리일 뿐이고, 문자 리스트는 리스트일 뿐입니다. +<> #=> "abc" +[?a, ?b, ?c] #=> 'abc' + +# Elixir에서 `?a`는 문자 `a`에 대한 ASCII 정수를 반환합니다. +?a #=> 97 + +# 리스트를 연결하려면 `++`를 사용하고, 바이너리를 연결하려면 `<>`를 사용하십시오. +[1,2,3] ++ [4,5] #=> [1,2,3,4,5] +'hello ' ++ 'world' #=> 'hello world' + +<<1,2,3>> <> <<4,5>> #=> <<1,2,3,4,5>> +"hello " <> "world" #=> "hello world" + +# 범위는 `start..end`(양쪽 포함)로 표시됩니다. +1..10 #=> 1..10 +lower..upper = 1..10 # 범위에서도 패턴 매칭을 사용할 수 있습니다. +[lower, upper] #=> [1, 10] + +# 맵은 키-값 쌍입니다. +genders = %{"david" => "male", "gillian" => "female"} +genders["david"] #=> "male" + +# 원자 키가 있는 맵은 다음과 같이 사용할 수 있습니다. +genders = %{david: "male", gillian: "female"} +genders.gillian #=> "female" + +## --------------------------- +## -- 연산자 +## --------------------------- + +# 일부 수학 +1 + 1 #=> 2 +10 - 5 #=> 5 +5 * 2 #=> 10 +10 / 2 #=> 5.0 + +# Elixir에서 `/` 연산자는 항상 부동 소수점을 반환합니다. + +# 정수 나눗셈을 하려면 `div`를 사용하십시오. +div(10, 2) #=> 5 + +# 나눗셈 나머지를 얻으려면 `rem`을 사용하십시오. +rem(10, 3) #=> 1 + +# 부울 연산자도 있습니다: `or`, `and` 및 `not`. +# 이러한 연산자는 첫 번째 인수로 부울을 예상합니다. +true and true #=> true +false or true #=> true +# 1 and true +#=> ** (BadBooleanError) "and"의 왼쪽에 부울이 예상되었지만 다음을 받았습니다: 1 + +# Elixir는 또한 모든 유형의 인수를 허용하는 `||`, `&&` 및 `!`를 제공합니다. +# `false`와 `nil`을 제외한 모든 값은 true로 평가됩니다. +1 || true #=> 1 +false && 1 #=> false +nil && 20 #=> nil +!true #=> false + +# 비교를 위해 다음이 있습니다: `==`, `!=`, `===`, `!==`, `<=`, `>=`, `<` 및 `>` +1 == 1 #=> true +1 != 1 #=> false +1 < 2 #=> true + +# `===`와 `!==`는 정수와 부동 소수점을 비교할 때 더 엄격합니다: +1 == 1.0 #=> true +1 === 1.0 #=> false + +# Elixir 연산자는 인수에 대해 엄격하지만, +# 다른 데이터 유형에서 작동하는 비교 연산자는 예외입니다: +1 < :hello #=> true + +# 이것은 혼합 유형의 컬렉션을 빌드할 수 있게 합니다: +["string", 123, :atom] + +# 모든 데이터 유형의 전체 순서가 있지만, +# Joe Armstrong의 말을 인용하면: "실제 순서는 중요하지 않지만, +# 전체 순서가 잘 정의되어 있다는 것이 중요합니다." + +## --------------------------- +## -- 제어 흐름 +## --------------------------- + +# `if` 표현식 +if false do + "이것은 절대 보이지 않을 것입니다" +else + "이것은 보일 것입니다" +end + +# 패턴 매칭을 기억하십니까? Elixir의 많은 제어 흐름 구조는 패턴 매칭에 의존합니다. + +# `case`는 값을 많은 패턴과 비교할 수 있게 합니다: +case {:one, :two} do + {:four, :five} -> + "이것은 일치하지 않을 것입니다" + {:one, x} -> + "이것은 일치하고 이 절에서 `x`를 `:two`에 바인딩합니다" + _ -> + "이것은 모든 값과 일치합니다" +end + +# 필요하지 않은 경우 값을 `_`에 바인딩하는 것이 일반적입니다. +# 예를 들어, 리스트의 머리만 중요한 경우: +[head | _] = [1,2,3] +head #=> 1 + +# 가독성을 높이기 위해 다음을 수행할 수 있습니다: +[head | _tail] = [:a, :b, :c] +head #=> :a + +# `cond`는 동시에 많은 조건을 확인할 수 있게 합니다. +# 많은 `if` 표현식을 중첩하는 대신 `cond`를 사용하십시오. +cond do + 1 + 1 == 3 -> + "나는 절대 보이지 않을 것입니다" + 2 * 5 == 12 -> + "나도 마찬가지" + 1 + 2 == 3 -> + "하지만 나는 보일 것입니다" +end + +# 마지막 조건을 `true`와 같게 설정하는 것이 일반적이며, 항상 일치합니다. +cond do + 1 + 1 == 3 -> + "나는 절대 보이지 않을 것입니다" + 2 * 5 == 12 -> + "나도 마찬가지" + true -> + "하지만 나는 보일 것입니다 (이것은 본질적으로 else입니다)" +end + +# `try/catch`는 throw된 값을 잡는 데 사용되며, 값이 잡혔는지 여부에 관계없이 호출되는 `after` 절도 지원합니다. +try do + throw(:hello) +catch + message -> "Got #{message}." +after + IO.puts("I'm the after clause.") +end +#=> I'm the after clause +# "Got :hello" + +## --------------------------- +## -- 모듈 및 함수 +## --------------------------- + +# 익명 함수 (점 참고) +square = fn(x) -> x * x end +square.(5) #=> 25 + +# 또한 많은 절과 가드를 허용합니다. +# 가드는 패턴 매칭을 미세 조정할 수 있게 하며, +# `when` 키워드로 표시됩니다: +f = fn + x, y when x > 0 -> x + y + x, y -> x * y +end + +f.(1, 3) #=> 4 +f.(-1, 3) #=> -3 + +# Elixir는 또한 많은 내장 함수를 제공합니다. +# 이러한 함수는 현재 범위에서 사용할 수 있습니다. +is_number(10) #=> true +is_list("hello") #=> false +elem({1,2,3}, 0) #=> 1 + +# 여러 함수를 모듈로 그룹화할 수 있습니다. 모듈 내에서 `def`를 사용하여 +# 함수를 정의합니다. +defmodule Math do + def sum(a, b) do + a + b + end + + def square(x) do + x * x + end +end + +Math.sum(1, 2) #=> 3 +Math.square(3) #=> 9 + +# 간단한 Math 모듈을 컴파일하려면 `math.ex`로 저장하고 터미널에서 `elixirc`를 사용하십시오: elixirc math.ex + +# 모듈 내에서 `def`로 함수를 정의하고 `defp`로 비공개 함수를 정의할 수 있습니다. +# `def`로 정의된 함수는 다른 모듈에서 호출할 수 있으며, +# 비공개 함수는 로컬에서만 호출할 수 있습니다. +defmodule PrivateMath do + def sum(a, b) do + do_sum(a, b) + end + + defp do_sum(a, b) do + a + b + end +end + +PrivateMath.sum(1, 2) #=> 3 +# PrivateMath.do_sum(1, 2) #=> ** (UndefinedFunctionError) + +# 함수 선언은 또한 가드와 여러 절을 지원합니다. +# 여러 절이 있는 함수가 호출되면, 절을 만족하는 첫 번째 함수가 호출됩니다. +# 예: area({:circle, 3})를 호출하면 아래에 정의된 두 번째 area 함수가 호출됩니다. 첫 번째가 아닙니다: +defmodule Geometry do + def area({:rectangle, w, h}) do + w * h + end + + def area({:circle, r}) when is_number(r) do + 3.14 * r * r + end +end + +Geometry.area({:rectangle, 2, 3}) #=> 6 +Geometry.area({:circle, 3}) #=> 28.25999999999999801048 +# Geometry.area({:circle, "not_a_number"}) +#=> ** (FunctionClauseError) Geometry.area/1에서 일치하는 함수 절 없음 + +# 불변성으로 인해 재귀는 Elixir의 큰 부분입니다. +defmodule Recursion do + def sum_list([head | tail], acc) do + sum_list(tail, acc + head) + end + + def sum_list([], acc) do + acc + end +end + +Recursion.sum_list([1,2,3], 0) #=> 6 + +# Elixir 모듈은 속성을 지원하며, 내장 속성이 있고 사용자 지정 속성을 추가할 수도 있습니다. +defmodule MyMod do + @moduledoc """ + 이것은 예제 모듈의 내장 속성입니다. + """ + + @my_data 100 # 이것은 사용자 지정 속성입니다. + IO.inspect(@my_data) #=> 100 +end + +# 파이프 연산자 |>는 표현식의 출력을 함수의 첫 번째 매개변수로 전달할 수 있게 합니다. + +Range.new(1,10) +|> Enum.map(fn x -> x * x end) +|> Enum.filter(fn x -> rem(x, 2) == 0 end) +#=> [4, 16, 36, 64, 100] + +## --------------------------- +## -- 구조체 및 예외 +## --------------------------- + +# 구조체는 기본값, 컴파일 타임 보장 및 다형성을 Elixir에 제공하는 맵 위의 확장입니다. +defmodule Person do + defstruct name: nil, age: 0, height: 0 +end + +joe_info = %Person{ name: "Joe", age: 30, height: 180 } +#=> %Person{age: 30, height: 180, name: "Joe"} + +# 이름 값에 액세스 +joe_info.name #=> "Joe" + +# 나이 값 업데이트 +older_joe_info = %{ joe_info | age: 31 } +#=> %Person{age: 31, height: 180, name: "Joe"} + +# `rescue` 키워드가 있는 `try` 블록은 예외를 처리하는 데 사용됩니다. +try do + raise "some error" +rescue + RuntimeError -> "rescued a runtime error" + _error -> "this will rescue any error" +end +#=> "rescued a runtime error" + +# 모든 예외에는 메시지가 있습니다. +try do + raise "some error" +rescue + x in [RuntimeError] -> + x.message +end +#=> "some error" + +## --------------------------- +## -- 동시성 +## --------------------------- + +# Elixir는 동시성을 위해 액터 모델에 의존합니다. Elixir에서 동시 프로그램을 작성하는 데 필요한 모든 것은 세 가지 기본 요소입니다: 프로세스 생성, 메시지 전송 및 메시지 수신. + +# 새 프로세스를 시작하려면 함수를 인수로 받는 `spawn` 함수를 사용합니다. +f = fn -> 2 * 2 end #=> #Function +spawn(f) #=> #PID<0.40.0> + +# `spawn`은 pid(프로세스 식별자)를 반환하며, 이 pid를 사용하여 프로세스에 메시지를 보낼 수 있습니다. 메시지 전달을 위해 `send` 연산자를 사용합니다. +# 이 모든 것이 유용하려면 메시지를 받을 수 있어야 합니다. 이것은 `receive` 메커니즘으로 달성됩니다: + +# `receive do` 블록은 메시지를 수신하고 수신될 때 처리하는 데 사용됩니다. `receive do` 블록은 수신된 메시지 하나만 처리합니다. 여러 메시지를 처리하려면 `receive do` 블록이 있는 함수가 재귀적으로 자신을 호출하여 다시 `receive do` 블록으로 들어가야 합니다. + +defmodule Geometry do + def area_loop do + receive do + {:rectangle, w, h} -> + IO.puts("Area = #{w * h}") + area_loop() + {:circle, r} -> + IO.puts("Area = #{3.14 * r * r}") + area_loop() + end + end +end + +# 모듈을 컴파일하고 셸에서 `area_loop`를 평가하는 프로세스를 생성합니다. +pid = spawn(fn -> Geometry.area_loop() end) #=> #PID<0.40.0> +# 또는 +pid = spawn(Geometry, :area_loop, []) + +# 수신 문에서 패턴과 일치하는 `pid`에 메시지를 보냅니다. +send pid, {:rectangle, 2, 3} +#=> Area = 6 +# {:rectangle,2,3} + +send pid, {:circle, 2} +#=> Area = 12.56000000000000049738 +# {:circle,2} + +# 셸도 프로세스이며, `self`를 사용하여 현재 pid를 얻을 수 있습니다. +self() #=> #PID<0.27.0> + +## --------------------------- +## -- 에이전트 +## --------------------------- + +# 에이전트는 일부 변경되는 값을 추적하는 프로세스입니다. + +# `Agent.start_link`로 에이전트를 생성하고 함수를 전달합니다. +# 에이전트의 초기 상태는 해당 함수가 반환하는 모든 것입니다. +{:ok, my_agent} = Agent.start_link(fn -> ["red", "green"] end) + +# `Agent.get`은 에이전트 이름과 현재 상태가 전달되는 `fn`을 받습니다. +# 해당 `fn`이 반환하는 모든 것이 반환됩니다. +Agent.get(my_agent, fn colors -> colors end) #=> ["red", "green"] + +# 동일한 방식으로 에이전트의 상태를 업데이트합니다. +Agent.update(my_agent, fn colors -> ["blue" | colors] end) +``` + +## 참조 + +* [시작 가이드](https://elixir-lang.org/getting-started/introduction.html) [Elixir 웹사이트](https://elixir-lang.org)에서 +* [Elixir 문서](https://elixir-lang.org/docs.html) +* Dave Thomas의 ["Programming Elixir"](https://pragprog.com/book/elixir/programming-elixir) +* [Elixir 치트 시트](https://media.pragprog.com/titles/elixir/ElixirCheat.pdf) +* Fred Hebert의 ["Learn You Some Erlang for Great Good!"](https://learnyousomeerlang.com/) +* Joe Armstrong의 ["Programming Erlang: Software for a Concurrent World"](https://pragprog.com/book/jaerlang2/programming-erlang) +* [Elixir 소개](https://learn-elixir.com/) \ No newline at end of file diff --git a/ko/elm.md b/ko/elm.md new file mode 100644 index 0000000000..4364b6288f --- /dev/null +++ b/ko/elm.md @@ -0,0 +1,349 @@ +--- +name: Elm +contributors: + - ["Max Goldstein", "http://maxgoldste.in/"] +filename: learnelm.elm +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Elm은 (클라이언트 측) JavaScript로 컴파일되는 함수형 반응형 프로그래밍 언어입니다. Elm은 정적으로 유형이 지정되므로 컴파일러가 대부분의 오류를 즉시 포착하고 명확하고 이해하기 쉬운 오류 메시지를 제공합니다. Elm은 웹용 사용자 인터페이스 및 게임을 설계하는 데 적합합니다. + + +```haskell +-- 한 줄 주석은 두 개의 대시로 시작합니다. +{- 여러 줄 주석은 이와 같이 블록으로 묶을 수 있습니다. +{- 중첩될 수 있습니다. -} +-} + +{-- 기본 사항 --} + +-- 산술 +1 + 1 -- 2 +8 - 1 -- 7 +10 * 2 -- 20 + +-- 소수점이 없는 모든 숫자 리터럴은 Int 또는 Float일 수 있습니다. +33 / 2 -- 부동 소수점 나눗셈으로 16.5 +33 // 2 -- 정수 나눗셈으로 16 + +-- 지수 +5 ^ 2 -- 25 + +-- 부울 +not True -- False +not False -- True +1 == 1 -- True +1 /= 1 -- False +1 < 10 -- True + +-- 문자열 및 문자 +"This is a string because it uses double quotes." +'a' -- 작은따옴표 안의 문자 + +-- 문자열을 추가할 수 있습니다. +"Hello " ++ "world!" -- "Hello world!" + +{-- 목록, 튜플 및 레코드 --} + +-- 목록의 모든 요소는 동일한 유형이어야 합니다. +["the", "quick", "brown", "fox"] +[1, 2, 3, 4, 5] +-- 두 번째 예제는 두 개의 점으로도 작성할 수 있습니다. +List.range 1 5 + +-- 문자열처럼 목록을 추가합니다. +List.range 1 5 ++ List.range 6 10 == List.range 1 10 -- True + +-- 항목 하나를 추가하려면 "cons"를 사용하십시오. +0 :: List.range 1 5 -- [0, 1, 2, 3, 4, 5] + +-- 목록의 머리와 꼬리는 Maybe로 반환됩니다. 모든 값을 확인하여 null인지 확인하는 대신 누락된 값을 명시적으로 처리합니다. +List.head (List.range 1 5) -- Just 1 +List.tail (List.range 1 5) -- Just [2, 3, 4, 5] +List.head [] -- Nothing +-- List.functionName은 함수가 List 모듈에 있음을 의미합니다. + +-- 튜플의 모든 요소는 다른 유형일 수 있지만 튜플은 고정된 길이를 가집니다. +("elm", 42) + +-- 첫 번째 및 두 번째 함수로 쌍의 요소에 액세스합니다. +-- (이것은 바로 가기입니다. 잠시 후에 "실제 방법"을 설명하겠습니다.) +Tuple.first ("elm", 42) -- "elm" +Tuple.second ("elm", 42) -- 42 + +-- 빈 튜플 또는 "단위"는 때때로 자리 표시자로 사용됩니다. +-- 해당 유형의 유일한 값이며 "Unit"이라고도 합니다. +() + +-- 레코드는 튜플과 같지만 필드에 이름이 있습니다. 필드 순서는 중요하지 않습니다. 레코드 값은 콜론이 아닌 등호를 사용합니다. +{ x = 3, y = 7 } + +-- 점과 필드 이름으로 필드에 액세스합니다. +{ x = 3, y = 7 }.x -- 3 + +-- 또는 자체적으로 점과 필드 이름인 접근자 함수를 사용합니다. +.y { x = 3, y = 7 } -- 7 + +-- 레코드의 필드를 업데이트합니다. (이미 필드가 있어야 합니다.) +{ person | + name = "George" } + +-- 현재 값을 사용하여 한 번에 여러 필드를 업데이트합니다. +{ particle | + position = particle.position + particle.velocity, + velocity = particle.velocity + particle.acceleration } + +{-- 제어 흐름 --} + +-- If 문에는 항상 else가 있으며 분기는 동일한 유형이어야 합니다. +if powerLevel > 9000 then + "WHOA!" +else + "meh" + +-- If 문을 연결할 수 있습니다. +if n < 0 then + "n is negative" +else if n > 0 then + "n is positive" +else + "n is zero" + +-- case 문을 사용하여 다른 가능성에 대해 패턴 일치 +case aList of + [] -> "matches the empty list" + [x]-> "matches a list of exactly one item, " ++ toString x + x::xs -> "matches a list of at least one item whose head is " ++ toString x +-- 패턴 일치는 순서대로 진행됩니다. [x]를 마지막에 넣으면 x::xs도 일치하므로(xs는 빈 목록이 됨) 절대 일치하지 않습니다. 일치는 "폴스루"되지 않습니다. +-- 컴파일러는 누락되거나 추가된 경우를 알려줍니다. + +-- Maybe에서 패턴 일치. +case List.head aList of + Just x -> "The head is " ++ toString x + Nothing -> "The list was empty." + +{-- 함수 --} + +-- Elm의 함수 구문은 매우 최소화되어 있으며 괄호와 중괄호 대신 대부분 공백에 의존합니다. "return" 키워드는 없습니다. + +-- 이름, 인수, 등호 및 본문으로 함수를 정의합니다. +multiply a b = + a * b + +-- 인수를 전달하여 함수를 적용(호출)합니다(쉼표 필요 없음). +multiply 7 6 -- 42 + +-- 일부 인수만 전달하여 함수를 부분적으로 적용합니다. +-- 그런 다음 해당 함수에 새 이름을 지정합니다. +double = + multiply 2 + +-- 상수는 비슷하지만 인수가 없습니다. +answer = + 42 + +-- 다른 함수에 인수로 함수를 전달합니다. +List.map double (List.range 1 4) -- [2, 4, 6, 8] + +-- 또는 익명 함수를 작성합니다. +List.map (\a -> a * 2) (List.range 1 4) -- [2, 4, 6, 8] + +-- 한 가지 경우만 있는 경우 함수 정의에서 패턴 일치할 수 있습니다. +-- 이 함수는 두 개의 인수가 아닌 하나의 튜플을 사용합니다. +-- 이것이 일반적으로 튜플에서 값을 풀거나 추출하는 방법입니다. +area (width, height) = + width * height + +area (6, 7) -- 42 + +-- 중괄호를 사용하여 레코드 필드 이름과 패턴 일치합니다. +-- let을 사용하여 중간 값을 정의합니다. +volume {width, height, depth} = + let + area = width * height + in + area * depth + +volume { width = 3, height = 2, depth = 7 } -- 42 + +-- 함수는 재귀적일 수 있습니다. +fib n = + if n < 2 then + 1 + else + fib (n - 1) + fib (n - 2) + +List.map fib (List.range 0 8) -- [1, 1, 2, 3, 5, 8, 13, 21, 34] + +-- 또 다른 재귀 함수 (실제 코드에서는 List.length 사용). +listLength aList = + case aList of + [] -> 0 + x::xs -> 1 + listLength xs + +-- 함수 호출은 모든 중위 연산자보다 먼저 발생합니다. 괄호는 우선 순위를 나타냅니다. +cos (degrees 30) ^ 2 + sin (degrees 30) ^ 2 -- 1 +-- 먼저 degrees가 30에 적용되고, 결과가 삼각 함수에 전달되고, 제곱되고, 마지막으로 덧셈이 발생합니다. + +{-- 유형 및 유형 주석 --} + +-- 컴파일러는 프로그램의 모든 값의 유형을 추론합니다. +-- 유형은 항상 대문자입니다. x : T를 "x는 유형 T를 가짐"으로 읽습니다. +-- 몇 가지 일반적인 유형, Elm의 REPL에서 볼 수 있습니다. +5 : Int +6.7 : Float +"hello" : String +True : Bool + +-- 함수에도 유형이 있습니다. ->를 "goes to"로 읽습니다. 가장 오른쪽 유형을 반환 값의 유형으로 생각하고 다른 유형을 인수로 생각하십시오. +not : Bool -> Bool +round : Float -> Int + +-- 값을 정의할 때 그 위에 유형을 작성하는 것이 좋습니다. +-- 주석은 컴파일러에서 확인하는 문서 형식입니다. +double : Int -> Int +double x = x * 2 + +-- 함수 인수는 괄호로 전달됩니다. +-- 소문자 유형은 유형 변수입니다. 각 호출이 일관된 한 모든 유형이 될 수 있습니다. +List.map : (a -> b) -> List a -> List b +-- "List dot map has type a-goes-to-b, goes to list of a, goes to list of b." + +-- 세 가지 특수 소문자 유형이 있습니다: number, comparable 및 appendable. +-- 숫자는 Int 및 Float에 대한 산술을 사용할 수 있게 합니다. +-- comparable은 a < b와 같이 숫자와 문자열을 정렬할 수 있게 합니다. +-- appendable은 a ++ b로 결합할 수 있습니다. + +{-- 유형 별칭 및 사용자 지정 유형 --} + +-- 레코드나 튜플을 작성할 때 해당 유형이 이미 존재합니다. +-- (레코드 유형은 콜론을 사용하고 레코드 값은 등호를 사용합니다.) +origin : { x : Float, y : Float, z : Float } +origin = + { x = 0, y = 0, z = 0 } + +-- 유형 별칭으로 기존 유형에 멋진 이름을 지정할 수 있습니다. +type alias Point3D = + { x : Float, y : Float, z : Float } + +-- 레코드에 별칭을 지정하면 이름을 생성자 함수로 사용할 수 있습니다. +otherOrigin : Point3D +otherOrigin = + Point3D 0 0 0 + +-- 하지만 여전히 동일한 유형이므로 동일하게 취급할 수 있습니다. +origin == otherOrigin -- True + +-- 반면에 사용자 지정 유형을 정의하면 이전에 존재하지 않았던 유형이 생성됩니다. +-- 사용자 지정 유형은 여러 가능성 중 하나일 수 있기 때문에 그렇게 불립니다. +-- 각 가능성은 "유형 변형"으로 표시됩니다. +type Direction = + North | South | East | West + +-- 유형 변형은 알려진 유형의 다른 값을 가질 수 있습니다. 이것은 재귀적으로 작동할 수 있습니다. +type IntTree = + Leaf | Node Int IntTree IntTree +-- "Leaf"와 "Node"는 유형 변형입니다. 유형 변형 뒤에 오는 모든 것은 유형입니다. + +-- 유형 변형은 값이나 함수로 사용할 수 있습니다. +root : IntTree +root = + Node 7 Leaf Leaf + +-- 사용자 지정 유형(및 유형 별칭)은 유형 변수를 사용할 수 있습니다. +type Tree a = + Leaf | Node a (Tree a) (Tree a) +-- "The type tree-of-a is a leaf, or a node of a, tree-of-a, and tree-of-a." + +-- 사용자 지정 유형에서 패턴 일치 변형. 대문자 변형은 정확히 일치합니다. 소문자 변수는 무엇이든 일치합니다. 밑줄도 무엇이든 일치하지만 사용하지 않음을 의미합니다. +leftmostElement : Tree a -> Maybe a +leftmostElement tree = + case tree of + Leaf -> Nothing + Node x Leaf _ -> Just x + Node _ subtree _ -> leftmostElement subtree + +-- 이것이 언어 자체에 대한 거의 전부입니다. 이제 코드를 구성하고 실행하는 방법을 살펴보겠습니다. + +{-- 모듈 및 가져오기 --} + +-- 핵심 라이브러리는 모듈로 구성되며, 사용할 수 있는 모든 타사 라이브러리도 마찬가지입니다. 대규모 프로젝트의 경우 자신만의 모듈을 정의할 수 있습니다. + +-- 파일 상단에 이것을 넣으십시오. 생략하면 Main에 있습니다. +module Name where + +-- 기본적으로 모든 것이 내보내집니다. 내보내기를 명시적으로 지정할 수 있습니다. +module Name (MyType, myValue) where + +-- 한 가지 일반적인 패턴은 사용자 지정 유형을 내보내지만 유형 변형은 내보내지 않는 것입니다. 이것은 "불투명 유형"으로 알려져 있으며 라이브러리에서 자주 사용됩니다. + +-- 다른 모듈에서 코드를 가져와 이 모듈에서 사용합니다. +-- Dict를 범위에 배치하므로 Dict.insert를 호출할 수 있습니다. +import Dict + +-- Dict 모듈과 Dict 유형을 가져오므로 주석에 Dict.Dict라고 말할 필요가 없습니다. 여전히 Dict.insert를 사용할 수 있습니다. +import Dict exposing (Dict) + +-- 가져오기 이름 바꾸기. +import Graphics.Collage as C + +{-- 포트 --} + +-- 포트는 외부 세계와 통신할 것임을 나타냅니다. +-- 포트는 Main 모듈에서만 허용됩니다. + +-- 들어오는 포트는 유형 서명일 뿐입니다. +port clientID : Int + +-- 나가는 포트에는 정의가 있습니다. +port clientOrders : List String +port clientOrders = ["Books", "Groceries", "Furniture"] + +-- 자세한 내용은 다루지 않겠지만, 들어오는 포트에서 보내고 나가는 포트에서 받도록 JavaScript에서 콜백을 설정합니다. + +{-- 명령줄 도구 --} + +-- 파일 컴파일. +$ elm make MyFile.elm + +-- 처음 이 작업을 수행하면 Elm은 핵심 라이브러리를 설치하고 프로젝트에 대한 정보가 보관되는 elm-package.json을 생성합니다. + +-- 리액터는 파일을 컴파일하고 실행하는 서버입니다. +-- 파일 이름 옆에 있는 렌치를 클릭하여 시간 여행 디버거를 시작하십시오! +$ elm reactor + +-- 읽기-평가-인쇄 루프에서 간단한 표현식으로 실험합니다. +$ elm repl + +-- 패키지는 GitHub 사용자 이름과 리포지토리 이름으로 식별됩니다. +-- 새 패키지를 설치하고 elm-package.json에 기록합니다. +$ elm package install elm-lang/html + +-- 패키지 버전 간에 변경된 내용을 확인합니다. +$ elm package diff elm-lang/html 1.1.0 2.0.0 +-- Elm의 패키지 관리자는 의미 체계 버전 관리를 적용하므로 부 버전 범프는 빌드를 절대 중단하지 않습니다! +``` + +Elm 언어는 놀라울 정도로 작습니다. 이제 거의 모든 Elm 소스 코드를 살펴보고 무슨 일이 일어나고 있는지 대략적으로 알 수 있습니다. 그러나 오류에 강하고 리팩토링하기 쉬운 코드의 가능성은 무한합니다! + +다음은 몇 가지 유용한 자료입니다. + +* [Elm 웹사이트](http://elm-lang.org/). 포함: + * [설치 프로그램](http://elm-lang.org/install) 링크 + * [문서 가이드](http://elm-lang.org/docs), [구문 참조](http://elm-lang.org/docs/syntax) 포함 + * 많은 유용한 [예제](http://elm-lang.org/examples) + +* [Elm의 핵심 라이브러리](http://package.elm-lang.org/packages/elm-lang/core/latest/) 문서. 다음 사항에 유의하십시오: + * 기본적으로 가져오는 [기본 사항](http://package.elm-lang.org/packages/elm-lang/core/latest/Basics) + * 누락된 값이나 오류 처리에 일반적으로 사용되는 [Maybe](http://package.elm-lang.org/packages/elm-lang/core/latest/Maybe) 및 그 사촌 [Result](http://package.elm-lang.org/packages/elm-lang/core/latest/Result) + * [List](http://package.elm-lang.org/packages/elm-lang/core/latest/List), [Array](http://package.elm-lang.org/packages/elm-lang/core/latest/Array), [Dict](http://package.elm-lang.org/packages/elm-lang/core/latest/Dict) 및 [Set](http://package.elm-lang.org/packages/elm-lang/core/latest/Set)과 같은 데이터 구조 + * JSON [인코딩](http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Encode) 및 [디코딩](http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode) + +* [Elm 아키텍처](https://github.com/evancz/elm-architecture-tutorial#the-elm-architecture). 코드를 구성 요소로 구성하는 방법에 대한 예제가 포함된 Elm 제작자의 에세이. + +* [Elm 메일링 리스트](https://groups.google.com/forum/#!forum/elm-discuss). 모두 친절하고 도움이 됩니다. + +* [Elm의 범위](https://github.com/elm-guides/elm-for-js/blob/master/Scope.md#scope-in-elm) 및 [유형 주석 읽는 방법](https://github.com/elm-guides/elm-for-js/blob/master/How%20to%20Read%20a%20Type%20Annotation.md#how-to-read-a-type-annotation). JavaScript 개발자를 위해 작성된 Elm의 기본 사항에 대한 더 많은 커뮤니티 가이드. + +나가서 Elm을 작성하십시오! \ No newline at end of file diff --git a/ko/emacs.md b/ko/emacs.md new file mode 100644 index 0000000000..28fe716779 --- /dev/null +++ b/ko/emacs.md @@ -0,0 +1,236 @@ +--- +category: tool +name: Emacs +filename: emacs.txt +contributors: + - ["Joseph Riad", "https://github.com/Joseph-Riad"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Emacs는 "확장 가능하고 사용자 정의 가능한 디스플레이 편집기"로 시작하여 수년에 걸쳐 완전한 생태계로 성장했습니다. 일반적으로 다양한 도구에 위임되는 많은 작업을 Emacs 내에서 일관되고 친숙한 인터페이스로 수행할 수 있습니다. 예로는 디렉토리 관리, PDF 문서 보기, SSH를 통한 파일 편집, git 리포지토리 관리 등이 있습니다(목록은 상당히 깁니다). 간단히 말해, Emacs는 여러분이 원하는 대로 만들 수 있습니다. 사용자의 스펙트럼은 텍스트 파일을 편집하는 데 사용하는 사람부터 운영 체제를 거의 대체하는 데 사용하는 극단적인 순수주의자까지 다양합니다. + +Emacs는 텍스트 편집 및 텍스트 버퍼 관리에 맞춰진 많은 매크로가 있는 Lisp의 특수 방언인 Emacs Lisp(Elisp)를 통해 확장 가능합니다. Emacs에서 사용하는 모든 키(조합)는 Emacs Lisp 함수에 바인딩되며, 직접 작성한 함수를 포함하여 다른 함수로 다시 매핑할 수 있습니다. + +# 키 표기법 + +```text +Emacs 설명서와 커뮤니티는 일반적으로 Emacs 내에서 사용되는 다양한 키 조합을 참조하는 규칙을 사용합니다. 특히 Emacs에는 다른 키와 함께 눌러 동작을 수정하는 "수정자 키" 개념이 있습니다. + +이 표기법의 예는 "C-c"입니다. 이 키 조합에서 "C"는 수정자이며 "Ctrl" 키를 나타내고 "c"는 동작이 수정되는 키(리터럴 문자 "c")입니다. + +수정자 약어: +"C-" --> "CTRL" 키 +"M-" --> "메타" 키 (보통 "Alt" 키) +"s-" --> "슈퍼" 키 (Mac의 "Cmd" 키 및 PC의 "Windows" 키) + +여기서는 다루지 않을 덜 일반적으로 사용되는 다른 수정자가 있습니다. + +키 조합 "C-x C-s"는 "Ctrl+x"를 누른 다음 "Ctrl+s"를 누르는 것을 의미합니다. + +위의 수정자 외에도 특수 키 "Esc", "Return (Enter)" 및 "Shift"는 각각 "ESC", "RET" 및 "S"로 표시됩니다. +``` + +# 기본 Emacs 개념 + +여기서는 초보자에게 혼란스러울 수 있는 몇 가지 기본 Emacs 개념과 용어를 설명합니다(특히 Vim 용어에 익숙한 사람들에게). + + - Emacs가 편집하는 텍스트 덩어리를 **버퍼**라고 합니다. + - 버퍼는 반드시 디스크의 실제 파일에 해당하지는 않습니다. 메모리의 텍스트 덩어리일 수 있습니다. + - 버퍼가 디스크의 파일에 해당할 때, 우리는 버퍼가 해당 파일을 **방문**한다고 말합니다. + - Emacs는 일반적으로 한 번에 많은 버퍼를 엽니다. + - Emacs의 디스플레이는 다른 **창**으로 분할될 수 있습니다(운영 체제의 창과 혼동하지 마십시오. Emacs의 운영 체제 창에는 여러 Emacs 창이 있을 수 있습니다). + - Emacs의 운영 체제 창을 Emacs **프레임**이라고 합니다. 따라서 Emacs 설명서에서 새 프레임을 여는 것에 대해 이야기할 때, 이는 본질적으로 Emacs의 (다른) 인스턴스를 포함하는 새 OS *창*을 여는 것을 의미합니다. + - 일반적으로 잘라내기 및 붙여넣기로 알려진 개념은 Emacs 용어에서 각각 **죽이기** 및 **당기기**라고 합니다. + - 커서의 현재 위치를 Emacs에서 **포인트**라고 합니다. 기술적으로 **포인트**는 커서가 현재 있는 문자 바로 앞의 위치로 정의됩니다. + - 마지막으로, 각 버퍼에는 여러 **모드**가 연결될 수 있습니다: **주 모드** 및 여러 **부 모드**. + - **주 모드**는 현재 선택된 버퍼에서 Emacs의 주요 동작을 정의합니다. 이것은 대략 파일 유형으로 생각할 수 있습니다. 예를 들어, Python 파일을 편집하는 경우 주 모드는 (기본적으로) `python-mode`이며, 이로 인해 Emacs는 Python 구문을 강조 표시하고 Python 코드에서 구문적으로 필요한 대로 코드 블록을 자동으로 들여쓰기 및 내어쓰기합니다. + - **부 모드**는 동작의 미묘한 변경을 정의하며, 여러 부 모드가 동일한 버퍼에서 동시에 활성화될 수 있습니다. 예제 부 모드는 버퍼에서 철자 오류를 자동으로 강조 표시하는 `flyspell-mode`입니다. + +# 탐색 기본 사항 + +```text +Emacs의 GUI 버전은 기존 GUI 텍스트 편집기에서 예상하는 것처럼 마우스를 사용하여 탐색할 수 있습니다. + +여기서의 목표는 생산성을 엄청나게 향상시키는 키보드만 사용하여 탐색하는 데 초점을 맞추는 것입니다. + + +* 줄 이동 + +C-n --> 다음 줄 +C-p --> 이전 줄 + +* 문자 이동 + +C-f --> 한 문자 앞으로 이동 +C-b --> 한 문자 뒤로 이동 + +* 단어 이동 + +M-f --> 한 단어 앞으로 이동 +M-b --> 한 단어 뒤로 이동 + +* 문장 이동 + +M-a --> 문장 시작으로 이동 +M-e --> 문장 끝으로 이동 + +* 줄 시작 및 끝 + +C-a --> 줄 시작으로 이동 +C-e --> 줄 끝으로 이동 + +* 버퍼 시작 및 끝 + +M-< ("Meta+Shift+,") --> 버퍼 시작으로 이동 +M-> ("Meta+Shift+.") --> 버퍼 끝으로 이동 + +* 화면 이동 + +C-v --> 한 화면 아래로 스크롤 (이전 화면의 마지막 두 줄은 부드러운 전환을 위해 겹침으로 유지됨) +M-v --> 한 화면 위로 스크롤 (위와 동일하지만 첫 두 줄 포함) + +* 화면 중앙 정렬 + +C-l --> 현재 줄을 화면 중앙으로 이동 + +위의 키 조합은 실제로 누른 횟수에 따라 다른 상태를 순환합니다. + +C-l --> 현재 줄을 화면 중앙으로 이동 +C-l C-l --> 현재 줄을 화면 상단으로 이동 +C-l C-l C-l --> 첫 번째 C-l을 누르기 전의 위치로 현재 줄의 위치 복원 + +"C-l"을 4번째 누르면 현재 줄을 중앙에 맞추는 것으로 다시 순환합니다. + +* 이동 명령 반복 + +대부분의 이동 명령은 "다음 명령을 여러 번 반복"하라는 숫자 접두사 인수를 사용합니다. + +예: + +C-u 3 C-p --> 3줄 위로 이동 +C-u 5 C-f --> 5자 앞으로 이동 + +한 가지 주목할 만한 예외는 화면 스크롤 명령입니다: + +C-u 3 C-v --> 3줄 아래로 스크롤 (커서 위치 유지) +``` + +보너스: 위의 많은 탐색 명령은 Bash의 기본 탐색 명령입니다(예: Bash 명령을 입력하는 동안 "C-b"를 누르면 한 문자 뒤로 이동합니다). + +# 파일 편집 기본 사항 + +```text +* Emacs 종료 [ 이제 Emacs를 종료하는 방법을 모른다고 말할 수 없습니다 :-) ] + +C-x C-c --> Emacs를 종료하고 저장되지 않은 파일을 저장하라는 메시지가 표시됩니다(파일을 방문하지 않는 버퍼는 클라이언트-서버 모드에서 실행 중이 아닌 한 단순히 삭제됩니다). + +* 버퍼 저장 + +C-x C-s --> 현재 버퍼를 저장합니다. 파일을 방문하지 않는 경우 버퍼를 저장하는 데 사용할 파일 이름을 묻는 메시지가 표시됩니다. + +* 버퍼 내에서 검색 + +C-s --> 버퍼 내에서 앞으로 검색합니다. 검색은 기본적으로 증분 및 대소문자를 구분하지 않습니다. + C-s를 눌러 다음 일치 항목으로 이동합니다. + "RET"을 누르면 포인트가 현재 강조 표시된 단어로 이동하고 검색이 종료됩니다. +C-r --> C-s와 동일하지만 뒤로 검색합니다. + +C-_ 또는 C-/ --> 마지막 작업을 취소합니다. 실행 취소 트리를 위로 이동하려면 계속 누르십시오. +M-? 또는 M-_ --> 이전 변경 사항을 다시 실행합니다. + +"실행 취소" 및 "다시 실행" 명령은 해당 작업을 여러 번 실행 취소하거나 다시 실행하기 위해 접두사 숫자 인수를 사용할 수 있습니다: + +C-u 3 C-_ --> 마지막 3가지 변경 사항을 실행 취소합니다. +``` + +# Elisp 함수 실행 + +```text +"M-x"를 통해 현재 로드된 Elisp 함수(직접 작성한 함수 포함)를 실행할 수 있습니다. + +M-x RET --> 실행할 함수 이름을 묻는 메시지가 표시됩니다(탭 완성이 가능합니다). + +예: + +M-x RET search-forward-regexp RET --> 정규 표현식을 묻는 메시지가 표시되고 버퍼에서 앞으로 검색합니다. +``` + +# Emacs 구성 + +Emacs는 Elisp를 사용하여 구성됩니다. 시작 시 `~/.emacs` 또는 `~/.emacs.d/init.el`에서 구성 파일을 찾습니다. 여기서 `~`는 홈 디렉토리를 나타냅니다. Windows를 사용하는 경우 구성 파일의 적절한 위치에 대해서는 [이 문서](https://www.gnu.org/software/emacs/manual/html_node/efaq-w32/Location-of-init-file.html)를 참조하십시오. + +# Emacs 내의 Vim + +Vim에서 Emacs로 전환을 고려하고 있고 Emacs의 비모달 특성에 거부감이 있다면, Emacs 내에서 많은 Vim 개념을 가질 수 있는 `evil-mode`라는 Emacs 확장이 있습니다. 다음은 `evil-mode`에 의해 Emacs에 추가된 몇 가지 사항입니다: + + - 모달 편집: Vim과 같이 일반, 삽입, 비주얼 및 블록 비주얼 모드를 사용할 수 있습니다. 또한 이동 및 탐색이 Emacs 바인딩을 따르는 "Emacs" 모드가 있습니다. + - 일반 모드에서 Vim과 동일한 이동 키 + - 리더 키 조합 + - 일반 모드에서 ":"를 누르면 명령(시스템 명령 포함)을 실행할 수 있습니다. + +제 경험상 `evil-mode`는 전환을 원활하게 하고 Vim의 더 직관적이고 인체공학적인 키 바인딩과 Emacs의 무한한 힘을 혼합하여 진정으로 우수한 편집 경험을 제공하는 데 도움이 됩니다. + +# 검색 가능한 도움말 + +Emacs에는 항상 새로운 기능을 발견할 수 있는 매우 강력한 도움말 시스템이 있습니다. + +```text +특정 주제에 대한 도움말 얻기. 함수 및 변수 이름에 대해 탭 완성이 가능합니다. + +C-h f RET --> elisp 함수 이름을 묻는 메시지가 표시되고 + 소스 코드에 대한 클릭 가능한 링크와 함께 도움말 텍스트를 표시합니다. +C-h v RET --> 변수에 대해 위와 동일 + +C-h k RET --> 키 조합을 입력할 수 있으며 + 바인딩된 elisp 함수의 이름을 표시합니다. + +도움말 검색: + +C-h a --> 도움말 시스템에서 명령을 검색할 문자열을 묻는 메시지가 표시됩니다. + Unix 시스템의 'apropos' 또는 'man -k' 명령과 유사합니다. + +튜토리얼 시작: + +C-h C-t --> 기본 Emacs 기능에 익숙해지도록 설계된 튜토리얼을 시작합니다. +``` + +# Emacs "킬러 앱" + +위에서 암시했듯이 Emacs 기능은 단순한 텍스트 편집기를 훨씬 뛰어넘습니다. 여기서는 매우 강력하고 인기가 있으며 그 자체로 흥미로울 수 있는 몇 가지 Emacs "앱"을 나열하겠습니다. + +## Org + +기술적으로 `org-mode`는 조직 도구를 제공하는 버퍼 편집을 위한 주 모드입니다. Org가 할 수 있는 일을 간결하게 설명하기는 매우 어렵습니다. 왜냐하면 그것은 다양한 사람들에게 다양한 용도로 사용되는 거대한 도구이기 때문입니다. 제가 사용하는 주요 기능을 간략하게 설명하겠습니다. + + - 개념을 쉽게 개요화하고 구성하기 위해 파일을 섹션 및 하위 섹션으로 나눕니다. + - 개요의 다른 제목은 접거나 확장할 수 있으므로 집중해야 할 것에 집중하고 산만함을 제거할 수 있습니다. + - Org 내에서 TODO 목록을 유지할 수 있습니다. + - 여러 파일의 TODO 목록을 의제로 컴파일할 수 있습니다. + - 각 TODO 작업에 소요된 시간을 추적합니다. + - 일반 텍스트로 테이블 관리(스프레드시트와 유사한 기능 포함) + - `org-babel` 확장을 사용하여 파일에서 코드 블록을 작성하고 실행합니다. 결과는 캡처되어 파일 자체 내에서 재사용할 수 있습니다. 모든 언어에 대한 Jupyter 노트북을 생각하십시오. + - 파일 내에서 인라인 이미지 및 LaTeX 수식을 이미지로 표시합니다(훌륭한 메모 작성 시스템 및/또는 개인 위키에 적합). + - 파일을 다양한 형식(LaTeX, PDF, html 등)으로 내보냅니다. + +Org 모드는 생산성 무기고에 추가할 수 있는 매우 강력한 도구이며, 개인적으로는 Vim을 수년간 사용한 후 Emacs를 사용하기 시작한 이유였습니다. + +## Magit + +이것은 Emacs 내에서 `git`에 대한 프런트엔드입니다. 매우 직관적이고 검색 가능한 인터페이스를 특징으로 하면서도 청크 수준에서 커밋을 관리하고, 차이점을 검사하고, 리베이스하고, 체리픽하는 등 매우 강력한 기능을 노출합니다. 이 모든 것을 편집기 내에서 편안하게 할 수 있습니다. + +# 조언 한마디 + +Emacs 사용을 고려하고 있다면, 초보 사용자가 빠지는 일반적인 함정은 다른 사람의 구성 파일을 복사하여 그대로 사용하는 것입니다. 몇 가지 이유로 이 방법을 강력히 권장하지 않습니다: + + - 스스로 배우고 알아내는 것을 방해합니다. + - 다른 사람의 구성에는 아마도 그들에게 관련이 있지만 여러분에게는 필요하지 않거나 절대 사용하지 않을 많은 것들이 포함되어 있을 것입니다. + - 자신의 필요에 맞게 사용자 정의할 수 있는 텍스트 편집기를 갖는 목적을 무시합니다. + +제가 권장하는 것은 다른 사람의 구성을 보고 이해하고 자신에게 의미 있는 것만 적용하는 것입니다. 많은 YouTube 동영상, 스크린캐스트 또는 블로그 게시물을 통해 Emacs의 새로운 기능에 대해 알아볼 수 있으며, 그런 다음 구성 및 워크플로에 추가하는 방법을 직접 배울 수 있습니다. 이렇게 하면 Emacs에 대한 지식을 늘리면서 구성을 점진적으로 성장시킬 수 있습니다. + +# 추가 자료 + + - [GNU Emacs 설명서](https://www.gnu.org/software/emacs/manual/emacs.html) + - [Emacs Stack Exchange](https://emacs.stackexchange.com/) + - [Emacs 위키](https://www.emacswiki.org/emacs/EmacsWiki) \ No newline at end of file diff --git a/ko/factor.md b/ko/factor.md new file mode 100644 index 0000000000..afa552ed6e --- /dev/null +++ b/ko/factor.md @@ -0,0 +1,182 @@ +--- +name: Factor +contributors: + - ["hyphz", "http://github.com/hyphz/"] +filename: learnfactor.factor +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Factor는 Slava Pestov가 만든 Forth를 기반으로 한 현대적인 스택 기반 언어입니다. + +이 파일의 코드는 Factor에 입력할 수 있지만, 어휘 및 가져오기 헤더가 시작을 완전히 혼란스럽게 만들기 때문에 직접 가져올 수는 없습니다. + +```factor +! 이것은 주석입니다. + +! Forth와 마찬가지로 모든 프로그래밍은 스택을 조작하여 수행됩니다. +! 리터럴 값을 명시하면 스택에 푸시됩니다. +5 2 3 56 76 23 65 ! 출력은 없지만 스택은 대화형 모드에서 인쇄됩니다. + +! 이러한 숫자는 왼쪽에서 오른쪽으로 스택에 추가됩니다. +! .s는 스택을 비파괴적으로 인쇄합니다. +.s ! 5 2 3 56 76 23 65 + +! 산술은 스택의 데이터를 조작하여 작동합니다. +5 4 + ! 출력 없음 + +! `.`는 스택에서 최상위 결과를 팝하고 인쇄합니다. +. ! 9 + +! 산술의 추가 예: +6 7 * . ! 42 +1360 23 - . ! 1337 +12 12 / . ! 1 +13 2 mod . ! 1 + +99 neg . ! -99 +-99 abs . ! 99 +52 23 max . ! 52 +52 23 min . ! 23 + +! 스택을 조작하기 위해 여러 단어가 제공되며, 이를 통칭하여 셔플 단어라고 합니다. + +3 dup - ! 최상위 항목 복제 (1번째가 이제 2번째와 같음): 3 - 3 +2 5 swap / ! 최상위 항목을 두 번째 요소와 교환: 5 / 2 +4 0 drop 2 / ! 최상위 항목 제거 (화면에 인쇄하지 않음): 4 / 2 +1 2 3 nip .s ! 두 번째 항목 제거 (drop과 유사): 1 3 +1 2 clear .s ! 전체 스택 지우기 +1 2 3 4 over .s ! 두 번째 항목을 맨 위로 복제: 1 2 3 4 3 +1 2 3 4 2 pick .s ! 세 번째 항목을 맨 위로 복제: 1 2 3 4 2 3 + +! 단어 만들기 +! `:` 단어는 `;` 단어를 볼 때까지 Factor를 컴파일 모드로 설정합니다. +: square ( n -- n ) dup * ; ! 출력 없음 +5 square . ! 25 + +! 단어가 무엇을 하는지 볼 수도 있습니다. +! \는 단어의 평가를 억제하고 대신 스택에 식별자를 푸시합니다. +\ square see ! : square ( n -- n ) dup * ; + +! 만들 단어의 이름 뒤에 대괄호 사이의 선언은 스택 효과를 제공합니다. +! 선언 내에서 원하는 이름을 사용할 수 있습니다: +: weirdsquare ( camel -- llama ) dup * ; + +! 단어의 스택 효과와 개수가 일치하는 경우: +: doubledup ( a -- b ) dup dup ; ! 오류: 스택 효과 선언이 잘못되었습니다. +: doubledup ( a -- a a a ) dup dup ; ! 확인 +: weirddoubledup ( i -- am a fish ) dup dup ; ! 또한 확인 + +! Factor가 Forth와 다른 점은 인용 부호를 사용하는 것입니다. +! 인용 부호는 값으로 스택에 푸시되는 코드 블록입니다. +! [는 인용 부호 모드를 시작하고 ]는 종료합니다. +[ 2 + ] ! 2를 더하는 인용 부호가 스택에 남습니다. +4 swap call . ! 6 + +! 따라서 고차 단어입니다. 수많은 고차 단어입니다. +2 3 [ 2 + ] dip .s ! 최상위 스택 값을 팝하고, 인용 부호를 실행하고, 다시 푸시합니다: 4 3 +3 4 [ + ] keep .s ! 최상위 스택 값을 복사하고, 인용 부호를 실행하고, 복사본을 푸시합니다: 7 4 +1 [ 2 + ] [ 3 + ] bi .s ! 각 인용 부호를 최상위 값에 대해 실행하고, 두 결과를 모두 푸시합니다: 3 4 +4 3 1 [ + ] [ + ] bi .s ! bi의 인용 부호는 스택의 더 깊은 곳에서 값을 가져올 수 있습니다: 4 5 ( 1+3 1+4 ) +1 2 [ 2 + ] bi@ .s ! 첫 번째 및 두 번째 값에 대해 인용 부호를 실행합니다. +2 [ + ] curry ! 지정된 값을 인용 부호의 시작 부분에 주입합니다: [ 2 + ]가 스택에 남습니다. + +! 조건문 +! 내장 값 f를 제외한 모든 값은 참입니다. +! 내장 값 t는 존재하지만 사용이 필수는 아닙니다. +! 조건문은 위의 조합기와 같은 고차 단어입니다. + +5 [ "Five is true" . ] when ! Five is true +0 [ "Zero is true" . ] when ! Zero is true +f [ "F is true" . ] when ! 출력 없음 +f [ "F is false" . ] unless ! F is false +2 [ "Two is true" . ] [ "Two is false" . ] if ! Two is true + +! 기본적으로 조건문은 테스트 중인 값을 소비하지만, 별표가 붙은 변형은 +! 참인 경우 그대로 둡니다: + +5 [ . ] when* ! 5 +f [ . ] when* ! 출력 없음, 빈 스택, f는 거짓이므로 소비됩니다. + + +! 루프 +! 짐작하셨겠지만.. 이것들도 고차 단어입니다. + +5 [ . ] each-integer ! 0 1 2 3 4 +4 3 2 1 0 5 [ + . ] each-integer ! 0 2 4 6 8 +5 [ "Hello" . ] times ! Hello Hello Hello Hello Hello + +! 다음은 목록입니다: +{ 2 4 6 8 } ! 한 항목으로 스택에 들어갑니다. + +! 목록을 반복합니다: +{ 2 4 6 8 } [ 1 + . ] each ! 3 5 7 9 인쇄 +{ 2 4 6 8 } [ 1 + ] map ! 스택에 { 3 5 7 9 }를 남깁니다. + +! 목록을 줄이거나 빌드하는 루프: +{ 1 2 3 4 5 } [ 2 mod 0 = ] filter ! 인용 부호가 참을 산출하는 목록 멤버만 유지합니다: { 2 4 } +{ 2 4 6 8 } 0 [ + ] reduce . ! 함수형 언어의 "fold"와 같습니다: 20 인쇄 (0+2+4+6+8) +{ 2 4 6 8 } 0 [ + ] accumulate . . ! reduce와 같지만 중간 값을 목록에 유지합니다: { 0 2 6 12 }를 인쇄한 다음 20을 인쇄합니다. +1 5 [ 2 * dup ] replicate . ! 인용 부호를 5번 반복하고 결과를 목록에 수집합니다: { 2 4 8 16 32 } 인쇄 +1 [ dup 100 < ] [ 2 * dup ] produce ! 첫 번째 인용 부호가 거짓을 반환할 때까지 두 번째 인용 부호를 반복하고 결과를 수집합니다: { 2 4 8 16 32 64 128 } + +! 다른 모든 방법이 실패하면 범용 while 루프: +1 [ dup 10 < ] [ "Hello" . 1 + ] while ! "Hello"를 10번 인쇄합니다. + ! 예, 읽기 어렵습니다. + ! 이것이 모든 변형 루프가 있는 이유입니다. + +! 변수 +! 일반적으로 Factor 프로그램은 모든 데이터를 스택에 유지해야 합니다. +! 명명된 변수를 사용하면 리팩토링이 더 어려워집니다(그리고 Factor라고 불리는 데는 이유가 있습니다). +! 전역 변수, 꼭 필요한 경우: + +SYMBOL: name ! name을 식별 단어로 생성합니다. +"Bob" name set-global ! 출력 없음 +name get-global . ! "Bob" + +! 명명된 지역 변수는 확장으로 간주되지만 사용할 수 있습니다. +! 인용 부호에서.. +[| m n ! 인용 부호는 최상위 두 스택 값을 m과 n으로 캡처합니다. + | m n + ] ! 읽기 + +! 또는 단어에서.. +:: lword ( -- ) ! 어휘 변수 확장을 호출하려면 이중 콜론 참고 + 2 :> c ! 불변 변수 c를 선언하여 2를 보유합니다. + c . ; ! 인쇄 + +! 이 방법으로 선언된 단어에서 스택 선언의 입력 쪽은 +! 의미가 있으며 스택 값이 캡처되는 변수 이름을 제공합니다. +:: double ( a -- result ) a 2 * ; + +! 변수는 이름이 느낌표로 끝나는 경우 변경 가능으로 선언됩니다. +:: mword2 ( a! -- x y ) ! 스택의 맨 위를 변경 가능한 변수 a에 캡처합니다. + a ! a 푸시 + a 2 * a! ! a에 2를 곱하고 결과를 a에 다시 저장합니다. + a ; ! a의 새 값을 푸시합니다. +5 mword2 ! 스택: 5 10 + +! 목록 및 시퀀스 +! 위에서 목록을 스택에 푸시하는 방법을 보았습니다. + +0 { 1 2 3 4 } nth ! 목록의 특정 멤버에 액세스: 1 +10 { 1 2 3 4 } nth ! 오류: 시퀀스 인덱스가 범위를 벗어났습니다. +1 { 1 2 3 4 } ?nth ! 인덱스가 범위 내에 있으면 nth와 동일: 2 +10 { 1 2 3 4 } ?nth ! 범위 밖에 있으면 오류 없음: f + +{ "at" "the" "beginning" } "Append" prefix ! { "Append" "at" "the" "beginning" } +{ "Append" "at" "the" } "end" suffix ! { "Append" "at" "the" "end" } +"in" 1 { "Insert" "the" "middle" } insert-nth ! { "Insert" "in" "the" "middle" } +"Concat" "enate" append ! "Concatenate" - 문자열도 시퀀스입니다. +"Concatenate" "Reverse " prepend ! "Reverse Concatenate" +{ "Concatenate " "seq " "of " "seqs" } concat ! "Concatenate seq of seqs" +{ "Connect" "subseqs" "with" "separators" } " " join ! "Connect subseqs with separators" + +! 그리고 메타를 원한다면 인용 부호는 시퀀스이며 분해할 수 있습니다.. +0 [ 2 + ] nth ! 2 +1 [ 2 + ] nth ! + +[ 2 + ] \ - suffix ! 인용 부호 [ 2 + - ] +``` + +## 추가 자료 + +* [Factor 문서](http://docs.factorcode.org/content/article-help.home.html) \ No newline at end of file diff --git a/ko/fish.md b/ko/fish.md new file mode 100644 index 0000000000..62f929cb9c --- /dev/null +++ b/ko/fish.md @@ -0,0 +1,339 @@ +--- +name: fish +contributors: + - ["MySurmise", "https://github.com/MySurmise"] + - ["Geo Maciolek", "https://github.com/GeoffMaciolek"] +filename: learn.fish +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Fish (**f**riendly **i**nteractive **sh**ell)는 이국적인 셸의 이름입니다. 이는 Bourne-Shell이나 C-Shell에서 파생되지 않은 구문을 가진 셸입니다. + +fish의 장점은 최신 셸에서 원하는 많은 기능이 기본적으로 제공되므로 zsh 및 oh-my-zsh와 같은 추가 소프트웨어를 설치할 필요가 없다는 것입니다. + +이러한 기능의 예로는 자동 제안, 24비트 색상, Man 페이지 완성(즉, fish가 man 페이지를 자동으로 구문 분석하고 명령에 대한 추가 옵션을 제안함) 또는 웹 페이지를 통해 옵션을 만드는 기능(GUI가 설치된 경우)이 있습니다. + +2005년 2월에 출시되었습니다. + +- [더 읽기](https://fishshell.com/docs/current/language.html) +- [설치 가이드](https://github.com/fish-shell/fish-shell#getting-fish) + + +## 가이드 + +최신 fish 셸이 있는지 확인하십시오. 이것은 버전 3.3.0으로 만들어졌습니다. 테스트하려면 다음을 입력하십시오: + +``` +> fish -v +``` + +fish 셸을 시작하려면 다음을 입력하십시오: + +``` +> fish +``` + +종료하려면 다음을 입력하십시오: + +``` +> exit +``` + +또는 Ctrl + D를 누르십시오. + +이제 바로 fish에서 한 가지 성가신 점이 있습니다. 환영 메시지입니다. 누가 필요하겠습니까, 그렇죠? 셸이 시작되면 다음을 입력하십시오: + +``` +> set -U fish_greeting "" +``` + +bash로 작성된 단일 명령을 해당 셸로 전환하지 않고 실행하려면 다음을 입력할 수 있습니다: + +``` +> bash -c 'echo "fish is better than bash"' +``` + +fish에서는 작은따옴표나 큰따옴표를 사용할 수 있습니다. +이스케이프 문자는 `\`입니다. + +구성 파일을 편집하여 fish 구성을 변경할 수 있습니다. + +``` +> vim ~/.config/fish/config.fish +``` + +또는 앞서 언급한 웹 설정을 엽니다: + +``` +> fish_config +``` + +fish PATH 변수에 무언가를 추가하는 것은 쉽습니다: + +``` +> fish_add_path ~/cowsay +``` + +bash로 그렇게 할 수 있습니까? 아니요, 항상 찾아봐야 합니다... 그냥 그렇게 쉽습니다! + +하지만 더 있습니다. 대부분의 fish 관련 명령은 예상대로 'fish'로 시작합니다. `fish`를 입력하고 TAB를 누르십시오. 그러면 fish의 멋진 기능 중 하나인 **그냥 작동하는** 자동 완성이 있습니다. +이제 TAB, Shift + TAB 및 화살표 키 로 탐색할 수 있습니다. + +도움이 필요하면 지역 정신과 의사에게 연락하거나 `man`을 입력하십시오. 그러면 해당 명령에 대한 설명서가 표시됩니다. 예를 들어: + +``` +> man set +``` + +마침내 fish를 시도했다면, fish에서 정말 멋진 다른 것을 볼 수 있습니다. 모든 것이 멋진 색상을 가지고 있고, 잘못 입력하면 실행하지 않아도 빨간색으로 표시되고, 따옴표 안에 무언가를 넣으면 어디서 끝나는지, 왜 그 따옴표가 작동하지 않는지 볼 수 있습니다. 왜냐하면 따옴표 안에 26번째 위치에 다른 따옴표가 있기 때문입니다. + +fish에는 와일드카드와 같은 더 멋진 것들이 있습니다. +예를 들어, 다음을 입력하십시오. + +``` +> ls *.fish +``` + +그러면 현재 디렉토리의 모든 fish 파일이 나열됩니다. + +명령당 여러 와일드카드를 가질 수 있으며, 재귀 와일드카드인 `**`도 있습니다. 이는 기본적으로 적합한 파일과 디렉토리를 포함한다는 의미입니다. +예를 들어 다음 명령은 (귀하의 경우) 다음을 반환합니다: + +``` +> ls ~/images/**.jpg + +~/images/nudes/pewdiepie.jpg +~/images/nudes/peppa.jpg +~/images/screenshots/2020-42-69.jpg +~/images/omegalul.jpg +``` + +물론, 명령의 출력을 다른 명령으로 파이프할 수도 있습니다. + +``` +>echo sick egg, nadia. no u do really goofy shit. | grep [udense] +``` + +파일에 쓰기: + +``` +>echo This\ is\ text > file.txt +``` + +(이스케이프 문자를 눈치채셨습니까?) +파일에 추가: + +``` +>echo This\ is\ a\ line >> file.txt +>echo This\ is\ a\ second\ line >> file.txt +``` + +자동 완성을 위해 항상 TAB를 누르십시오. fish가 얼마나 많은 것을 알고 있는지 놀랄 것입니다. + +변수를 사용하려면 bash와 같이 `$VAR`를 입력하십시오. + +``` +> echo "My home is $HOME" +My home is /home/myuser +``` + +여기서 작은따옴표와 큰따옴표의 차이점이 있습니다. 작은따옴표 안에 변수를 사용하면 대체되지 않습니다. + +``` +> echo 'My home is $HOME' +My home is $HOME +``` + +변수에 대한 자세한 내용은 나중에 설명합니다. + +두 명령을 실행하려면 `;`로 구분하십시오. + +``` +> echo Lol; echo this is fun +``` + +마지막 명령의 상태 코드는 `$status`에 저장됩니다. + +서로 의존하는 두 명령에 대해 &&를 사용할 수 있습니다. + +``` +> set var lol && echo $var +``` + +이전 명령이 성공한 경우 실행되는 `and`, 이전 명령이 성공하지 않은 경우 실행되는 `or`, 명령의 종료 상태를 반전시키는 `not`을 사용할 수도 있습니다. + +예를 들어: + +``` +> if not echo It's very late I should not waste my time with this + echo Nobody heard you + end +``` + +(물론 셸에서 이 모든 것을 할 수 있습니다.) + +--- +이제 fish의 스크립팅 부분을 시작하겠습니다. + +모든 셸과 마찬가지로 셸에서 명령을 실행할 수 있을 뿐만 아니라 `.fish` 파일로 저장된 파일로도 실행할 수 있습니다. +(fish 구문으로 `.sh` 파일을 실행할 수도 있지만, bash 스크립트 파일과 구별하기 위해 항상 fish 구문 스크립트에 `.fish`를 사용합니다.) + +```fish +# 이것은 fish의 주석입니다. +# +# 인터프리터를 지정하지 않고 파일을 실행하는 경우, +# 즉, 스크립트를 실행하는 소프트웨어를 지정하지 않는 경우, 셸에 +# 해당 인터프리터가 어디에 있는지 알려야 합니다. +# fish의 경우 스크립트의 첫 번째 줄에 다음 주석을 추가하기만 하면 됩니다: + +#!/bin/fish + +# 예를 들어 fish /path/to/script.fish를 통해 실행하는 경우 +# fish를 인터프리터로 지정했으므로 필요하지 않습니다. + +# 변수부터 시작하겠습니다. +# 프로그램 내에서 사용하려면 다음 구문을 사용할 수 있습니다. +set name 'My Variable' + +# 사용... +set -x name value +# 내보내기(eXport) 또는 +set -e name +# 지우기(Erase) + +# 공백으로 설정된 변수는 예상대로 두 개의 인수가 아닌 하나의 인수로 전송됩니다. +set turtlefolder 'Turtle Folder' +mkdir $turtlefolder + +# 이것은 예상대로 하나의 폴더를 생성하며, bash처럼 두 개가 아닙니다... +# 누가 그런 것을 원하겠습니까? 이것은 기능이지 버그가 아닙니다... + +# 목록을 변수로 가질 수도 있습니다. 이것은 실제로 의미가 있습니다. 왜냐하면 두 개의 폴더를 생성하는 변수를 원한다면 mkdir에 폴더 이름 목록을 주기만 하면 되기 때문입니다. + +# 그런 다음 다음을 사용하여 해당 목록의 항목 수를 계산할 수 있습니다: +count $PATH + +# 모든 것이 멋질 뿐만 아니라 fish에서는 모든 것이 목록입니다. +# 따라서 예를 들어 $PWD는 길이가 1인 목록입니다. +# 목록을 만들려면 set 명령에 여러 인수를 주기만 하면 됩니다: +set list entry1 entry2 entry3 + +# 그렇게 하면 기존 변수에 무언가를 추가할 수도 있습니다: +set PATH $PATH ~/cowsay/ + +# 하지만 앞서 언급했듯이 fish에서는 특히 더 간단한 방법이 있습니다. +# 모든 배열/목록과 마찬가지로 다음을 사용하여 액세스할 수 있습니다. +$listvar[2] + +# 다음을 사용하여 범위도 있습니다. +$listvar[1..5] + +# 그리고 다음과 같이 음수를 사용할 수 있습니다. +$listvar[-1] +# 예를 들어 마지막 요소에 액세스합니다. + +# 두 목록 변수를 결합할 때 멋진 데카르트 곱을 할 수도 있습니다: +set a 1 2 3 +set 1 a b c +echo $a$1 +# 출력: 1a 2a 3a 1b 2b 3b 1c 2c 3c + +# 물론, 분리하면 두 개의 개별 인수로 간주하고 차례로 에코합니다. 이것이 예상되는 동작입니다 @bash. + +# 다른 유용한 것들도 있습니다. 예를 들어 명령 대체가 있습니다. 예를 들어, 한 줄에 두 명령의 반환을 출력하고 싶을 때입니다. bash에서는 다음과 같이 합니다. +echo "`ls` is in $PWD" +# 또는 +echo "$(ls) is in $PWD" + +# 제 생각에는 불필요합니다. 저는 항상 잘못된 아포스트로피를 입력합니다. fish처럼 두 개의 괄호를 사용하지 않는 이유는 무엇입니까? +echo (ls) is in $PWD + +# 네, 그렇게 쉽습니다. 그리고 fish의 강조 표시 덕분에 올바르게 입력했는지 즉시 확인할 수 있습니다. + +# 그리고 예상대로, 제 생각에는 명령이 따옴표 안에서 작동하지 않습니다. 제 말은 왜 bash입니까? 좋습니다, 이제 그만하겠습니다. 하지만 fish에서는 다음과 같이 하십시오: +echo (ls)" is in $PWD" +# 또는 +set myvar "The file"(ls -a)" is in the directory $PWD" +# 문자열과 모든 파일이 있는 목록을 만듭니다. 시도해 보십시오. 멋지지 않습니까? + +# 그리고 이러한 변수를 별도의 인수로 분리하려면 그 사이에 공백을 두기만 하면 됩니다: + +set myvar "The files" (ls -a) " are in the directory $PWD" + +# if, else if, else가 있습니다. +if grep fish /etc/shells + echo Found fish +else if grep bash /etc/shells + echo Found bash +else + echo Got nothing +end + +# 한 가지 이상한 점은 물론 변수를 설정할 필요가 없기 때문에 한 개의 = 기호로 물건을 비교한다는 것입니다. 하지만 여전히... 그리고 키워드 "test": +if test $var = "test" + echo yes +else + echo no +end + +# 물론, 다음과 같은 switch case도 있습니다. +switch $OS +case Linux + echo "you're good" +case Windows + echo "install Gentoo" +case Arch + echo "I use arch btw" +case '*' + echo "what OS is $OS, please?" +end + + +# fish의 함수는 $argv 변수를 통해 인수를 받습니다. 구문은 다음과 같습니다: + +function print + echo $argv +end + +# "fish_exit"-이벤트와 같은 이벤트도 있습니다(이것이 무엇일까요, 흠?). + +# 함수 정의에 추가하여 사용할 수 있습니다: + +function on_exit --on-event fish_exit + echo fish is now exiting +end + +# 다음 명령으로 이벤트를 찾습니다. +functions --handlers + + +# functions 명령을 사용하여 함수에 대해 자세히 알아볼 수 있습니다. +# 예를 들어, 모든 함수의 소스 코드를 인쇄할 수 있습니다: +functions cd +functions print +# 또는 모든 함수의 이름을 가져옵니다: +functions + +# 물론 while 루프가 있습니다. +while test $var = lol + echo lol +end + +# for 루프 (와일드카드를 사용하면 훨씬 더 멋집니다): +for image in *.jpg + echo $image +end + +# Python의 range(0, 5)와 동등한 것이 있으므로 숫자로 표준 for 루프를 수행할 수도 있습니다: + +set files (ls) +for number in (seq 10) + echo "$files[$number] is file number $number" +end + +# 멋지다! + +# bashrc에 해당하는 것은 fishrc가 아니라 앞서 언급한 ~/.config/fish/의 config.fish 파일입니다. +# fish에 함수를 추가하려면 해당 디렉토리에 간단한 .fish 파일을 만들어야 합니다. 해당 함수를 config.fish에 붙여넣지 마십시오. 보기 흉합니다. +# 더 있으면 추가하십시오. 하지만 이것들이 가장 중요한 기본 사항입니다. \ No newline at end of file diff --git a/ko/forth.md b/ko/forth.md new file mode 100644 index 0000000000..3bafc6543d --- /dev/null +++ b/ko/forth.md @@ -0,0 +1,213 @@ +--- +name: Forth +contributors: + - ["Horse M.D.", "http://github.com/HorseMD/"] +filename: learnforth.fs +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Forth는 1970년대에 Charles H. Moore가 만들었습니다. 명령형, 스택 기반 언어 및 프로그래밍 환경으로, Open Firmware와 같은 프로젝트에서 사용됩니다. NASA에서도 사용됩니다. + +참고: 이 문서는 주로 Forth의 Gforth 구현에 중점을 두지만, 여기에 작성된 대부분의 내용은 다른 곳에서도 작동해야 합니다. + +```forth +\ 이것은 주석입니다. +( 이것도 주석이지만 단어를 정의할 때만 사용됩니다. ) + +\ --------------------------------- 전제 조건 ---------------------------------- + +\ Forth의 모든 프로그래밍은 매개변수 스택(더 일반적으로는 "스택"이라고 함)을 조작하여 수행됩니다. +5 2 3 56 76 23 65 \ ok + +\ 이러한 숫자는 왼쪽에서 오른쪽으로 스택에 추가됩니다. +.s \ <7> 5 2 3 56 76 23 65 ok + +\ Forth에서 모든 것은 단어 또는 숫자입니다. + +\ ------------------------------ 기본 산술 ------------------------------ + +\ 산술(실제로 데이터를 필요로 하는 대부분의 단어)은 스택의 데이터를 조작하여 작동합니다. +5 4 + \ ok + +\ `.`는 스택에서 최상위 결과를 팝합니다: +. \ 9 ok + +\ 산술의 추가 예: +6 7 * . \ 42 ok +1360 23 - . \ 1337 ok +12 12 / . \ 1 ok +13 2 mod . \ 1 ok + +99 negate . \ -99 ok +-99 abs . \ 99 ok +52 23 max . \ 52 ok +52 23 min . \ 23 ok + +\ ----------------------------- 스택 조작 ----------------------------- + +\ 당연히 스택으로 작업할 때 유용한 몇 가지 메서드가 필요합니다: + +3 dup - \ 최상위 항목 복제 (1번째가 이제 2번째와 같음): 3 - 3 +2 5 swap / \ 최상위 항목을 두 번째 요소와 교환: 5 / 2 +6 4 5 rot .s \ 최상위 3개 요소 회전: 4 5 6 +4 0 drop 2 / \ 최상위 항목 제거 (화면에 인쇄하지 않음): 4 / 2 +1 2 3 nip .s \ 두 번째 항목 제거 (drop과 유사): 1 3 + +\ ---------------------- 더 고급 스택 조작 ---------------------- + +1 2 3 4 tuck \ 최상위 항목을 두 번째 슬롯 아래에 복제: 1 2 4 3 4 ok +1 2 3 4 over \ 두 번째 항목을 맨 위로 복제: 1 2 3 4 3 ok +1 2 3 4 2 roll \ 해당 위치의 항목을 맨 위로 *이동*: 1 3 4 2 ok +1 2 3 4 2 pick \ 해당 위치의 항목을 맨 위로 *복제*: 1 2 3 4 2 ok + +\ 스택 인덱스를 참조할 때 0부터 시작합니다. + +\ ------------------------------ 단어 만들기 -------------------------------- + +\ `:` 단어는 Forth를 `;` 단어를 볼 때까지 컴파일 모드로 설정합니다. +: square ( n -- n ) dup * ; \ ok +5 square . \ 25 ok + +\ 단어가 무엇을 하는지 볼 수도 있습니다: +see square \ : square dup * ; ok + +\ -------------------------------- 조건문 -------------------------------- + +\ -1 == true, 0 == false. 그러나 0이 아닌 값은 일반적으로 참으로 처리됩니다: +42 42 = \ -1 ok +12 53 = \ 0 ok + +\ `if`는 컴파일 전용 단어입니다. `if` <할 일> `then` <나머지 프로그램>. +: ?>64 ( n -- n ) dup 64 > if ." Greater than 64!" then ; \ ok +100 ?>64 \ Greater than 64! ok + +\ Else: +: ?>64 ( n -- n ) dup 64 > if ." Greater than 64!" else ." Less than 64!" then ; +100 ?>64 \ Greater than 64! ok +20 ?>64 \ Less than 64! ok + +\ ------------------------------------ 루프 ----------------------------------- + +\ `?do`도 컴파일 전용 단어입니다. +: myloop ( -- ) 5 0 ?do cr ." Hello!" loop ; \ ok +myloop +\ Hello! +\ Hello! +\ Hello! +\ Hello! +\ Hello! ok + +\ `?do`는 스택에 두 개의 숫자를 예상합니다: 끝 숫자(제외)와 시작 숫자(포함). + +\ `i`를 사용하여 루프를 돌면서 인덱스 값을 얻을 수 있습니다: +: one-to-12 ( -- ) 12 0 do i . loop ; \ ok +one-to-12 \ 0 1 2 3 4 5 6 7 8 9 10 11 ok + +\ `do`는 비슷하게 작동하지만, 시작과 끝이 정확히 같으면 산술 언더플로가 발생할 때까지 영원히 반복됩니다. +: loop-forever 1 1 do i square . loop ; \ ok +loop-forever \ 1 4 9 16 25 36 49 64 81 100 ... + +\ `+loop`로 "단계"를 변경합니다: +: threes ( n n -- ) ?do i . 3 +loop ; \ ok +15 0 threes \ 0 3 6 9 12 ok + +\ `begin` <할 일> <플래그> `until`을 사용한 무한 루프: +: death ( -- ) begin ." Are we there yet?" 0 until ; \ ok + +\ ---------------------------- 변수 및 메모리 ---------------------------- + +\ `variable`을 사용하여 `age`를 변수로 선언합니다. +variable age \ ok + +\ 그런 다음 `!` 단어로 age에 21을 씁니다. +21 age ! \ ok + +\ 마지막으로 `@` "읽기" 단어를 사용하여 변수를 인쇄할 수 있습니다. 이 단어는 값을 스택에 추가하거나, 한 번에 읽고 인쇄하는 `?`를 사용할 수 있습니다. +age @ . \ 21 ok +age ? \ 21 ok + +\ 상수는 매우 유사하지만 메모리 주소를 신경 쓰지 않습니다: +100 constant WATER-BOILING-POINT \ ok +WATER-BOILING-POINT . \ 100 ok + +\ ----------------------------------- 배열 ----------------------------------- + +\ 배열을 만드는 것은 변수와 유사하지만 더 많은 메모리를 할당해야 합니다. + +\ `2 cells allot`을 사용하여 길이가 3인 배열을 만들 수 있습니다: +variable mynumbers 2 cells allot \ ok + +\ 모든 값을 0으로 초기화합니다. +mynumbers 3 cells erase \ ok + +\ 또는 `fill`을 사용할 수 있습니다: +mynumbers 3 cells 0 fill + +\ 또는 위의 모든 것을 건너뛰고 특정 값으로 초기화할 수 있습니다: +create mynumbers 64 , 9001 , 1337 , \ ok (마지막 `,`가 중요합니다!) + +\ ...와 동일합니다: + +\ 각 인덱스에 수동으로 값 쓰기: +64 mynumbers 0 cells + ! \ ok +9001 mynumbers 1 cells + ! \ ok +1337 mynumbers 2 cells + ! \ ok + +\ 특정 배열 인덱스에서 값 읽기: +0 cells mynumbers + ? \ 64 ok +1 cells mynumbers + ? \ 9001 ok + +\ 배열 조작을 위한 도우미 단어를 만들어 약간 단순화할 수 있습니다: +: of-arr ( n n -- n ) cells + ; \ ok +mynumbers 2 of-arr ? \ 1337 ok + +\ 쓰기에도 사용할 수 있습니다: +20 mynumbers 1 of-arr ! \ ok +mynumbers 1 of-arr ? \ 20 ok + +\ ------------------------------ 반환 스택 ------------------------------ + +\ 반환 스택은 단어가 다른 단어(예: 루프)를 실행할 때 포인터를 보유하는 데 사용됩니다. + +\ 이미 반환 스택의 맨 위를 복제하는 `i`를 보았습니다. `i`는 `r@`와 동일합니다. +: myloop ( -- ) 5 0 do r@ . loop ; \ ok + +\ 읽기뿐만 아니라 반환 스택에 추가하고 제거할 수도 있습니다: +5 6 4 >r swap r> .s \ 6 5 4 ok + +\ 참고: Forth는 단어 포인터에 반환 스택을 사용하므로 `>r` 뒤에는 항상 `r>`가 와야 합니다. + +\ ------------------------- 부동 소수점 연산 -------------------------- + +\ 대부분의 Forth는 부동 소수점 연산 사용을 피하는 경향이 있습니다. +8.3e 0.8e f+ f. \ 9.1 ok + +\ 일반적으로 부동 소수점을 다룰 때 단어 앞에 'f'를 붙입니다: +variable myfloatingvar \ ok +4.4e myfloatingvar f! \ ok +myfloatingvar f@ f. \ 4.4 ok + +\ --------------------------------- 최종 참고 사항 -------------------------------- + +\ 존재하지 않는 단어를 입력하면 스택이 비워집니다. 그러나 특별히 이를 위한 단어도 있습니다: +clearstack + +\ 화면 지우기: +page + +\ Forth 파일 로드: +\ s" forthfile.fs" included + +\ Forth의 사전에 있는 모든 단어를 나열할 수 있습니다(하지만 목록이 매우 큽니다!): +\ words + +\ Gforth 종료: +\ bye +``` + +## 추가 자료 + +* [Starting Forth](http://www.forth.com/starting-forth/) +* [Simple Forth](http://www.murphywong.net/hello/simple.htm) +* [Thinking Forth](http://thinking-forth.sourceforge.net/) \ No newline at end of file diff --git a/ko/fortran.md b/ko/fortran.md new file mode 100644 index 0000000000..4d712afe4b --- /dev/null +++ b/ko/fortran.md @@ -0,0 +1,477 @@ +--- +name: Fortran +contributors: + - ["Robert Steed", "https://github.com/robochat"] +filename: learnfortran.f90 +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Fortran은 가장 오래된 컴퓨터 언어 중 하나입니다. 1950년대에 IBM에서 수치 계산을 위해 개발되었습니다(Fortran은 "Formula Translation"의 약자입니다). 오래되었음에도 불구하고 날씨 예측과 같은 고성능 컴퓨팅에 여전히 사용됩니다. 그러나 언어는 수년에 걸쳐 상당히 변경되었지만 대부분 이전 버전과의 호환성을 유지합니다. 잘 알려진 버전은 FORTRAN 77, Fortran 90, Fortran 95, Fortran 2003, Fortran 2008, Fortran 2018 및 Fortran 2023입니다. + +이 개요에서는 가장 널리 구현된 최신 사양 중 하나인 Fortran 2008의 기능에 대해 설명하며, 이후 버전은 대체로 유사합니다(따라서 FORTRAN 77과는 매우 다릅니다). + +```fortran +! 이것은 주석입니다. + +program example ! example이라는 프로그램을 선언합니다. + + ! 코드는 프로그램, 함수, 서브루틴 또는 모듈 내에만 존재할 수 있습니다. + ! 들여쓰기는 필수는 아니지만 권장됩니다. + + ! 변수 선언 + ! =============== + + ! 모든 선언은 문 및 표현식 앞에 와야 합니다. + + implicit none ! 변수의 동적 선언을 방지합니다. + ! 권장! + ! implicit none은 모든 함수/프로그램/모듈에서 다시 선언해야 합니다... + + ! 중요 - Fortran은 대소문자를 구분하지 않습니다. + real z + REAL Z2 + + real :: v, x ! 경고: 기본 초기값은 컴파일러에 따라 다릅니다! + real :: a = 3, b = 2E12, c = 0.01 + integer :: i, j, k = 1, m + real, parameter :: PI = 3.14159265 ! 상수를 선언합니다. + logical :: y = .TRUE., n = .FALSE. ! 부울 유형입니다. + complex :: w = (0, 1) ! sqrt(-1) + character(len=3) :: month ! 3자 문자열입니다. + + ! 6개의 실수 배열을 선언합니다. + real :: array(6) + ! 배열을 선언하는 또 다른 방법입니다. + real, dimension(4) :: arrayb + ! 사용자 지정 인덱스가 있는 배열 -10에서 10까지(포함) + integer :: arrayc(-10:10) + ! 다차원 배열입니다. + real :: array2d(3, 2) + + ! '::' 구분 기호는 항상 필요한 것은 아니지만 권장됩니다. + + ! 다른 많은 변수 속성도 존재합니다: + real, pointer :: p ! 포인터를 선언합니다. + + integer, parameter :: LP = selected_real_kind(20) + real(kind=LP) :: d ! 긴 정밀도 변수입니다. + + ! 경고: 선언 중에 변수를 초기화하면 함수에서 문제가 발생합니다. + ! 이는 자동으로 'save' 속성을 의미하며, 값은 함수 호출 간에 저장됩니다. 일반적으로 상수를 제외하고 선언과 초기화 코드를 분리하십시오! + + ! 문자열 + ! ======= + + character :: a_char = 'i' + character(len=6) :: a_str = "qwerty" + character(len=30) :: str_b + character(len=*), parameter :: a_long_str = "This is a long string." + ! (len=*)를 사용하여 길이를 자동으로 계산할 수 있지만 상수에만 해당됩니다. + + str_b = a_str//" keyboard" ! // 연산자를 사용하여 문자열을 연결합니다. + + ! 할당 및 산술 + ! ======================= + + Z = 1 ! 위에 선언된 변수 z에 할당합니다. + j = 10 + 2 - 3 + a = 11.54/(2.3*3.1) + b = 2**3 ! 거듭제곱 + + ! 제어 흐름 문 및 연산자 + ! =================================== + + ! 한 줄 if 문 + if (z == a) b = 4 ! 조건은 항상 괄호가 필요합니다. + + if (z /= a) then ! z가 a와 같지 않음 + ! 다른 기호 비교는 < > <= >= == /=입니다. + b = 4 + else if (z .GT. a) then ! z가 a보다 큼 + ! 기호 연산자에 대한 텍스트 등가물은 .LT. .GT. .LE. .GE. .EQ. .NE.입니다. + b = 6 + else if (z < a) then ! 'then'은 이 줄에 있어야 합니다. + b = 5 ! 실행 블록은 새 줄에 있어야 합니다. + else + b = 10 + end if ! end 문에는 'if'가 필요합니다. + + if (.NOT. (x < c .AND. v >= a .OR. z == z)) then ! 부울 연산자입니다. + inner: if (.TRUE.) then ! if 구문에 이름을 지정할 수 있습니다. + b = 1 + end if inner ! 그런 다음 endif 문에 이름을 지정해야 합니다. + endif ! 'endif'는 'end if'와 동일합니다. + + i = 20 + select case (i) + case (0, 1) ! i == 0 또는 i == 1인 경우 + j = 0 + case (2:10) ! i가 2에서 10까지인 경우(포함). + j = 1 + case (11:) ! i>=11인 모든 경우 + j = 2 + case default + j = 3 + end select + + month = 'jan' + ! 조건은 정수, 논리 또는 문자 유형일 수 있습니다. + ! Select 구문도 이름을 지정할 수 있습니다. + monthly:select case(month) + case ("jan") + j = 0 + case default + j = -1 + end select monthly + + do i = 2, 10, 2 ! 2에서 10까지(포함) 2씩 증가하는 루프입니다. + innerloop: do j = 1, 3 ! 루프도 이름을 지정할 수 있습니다. + exit ! 루프를 종료합니다. + end do innerloop + cycle ! 다음 루프 반복으로 이동합니다. + end do + + ! Goto 문이 있지만 사용을 권장하지 않습니다. + goto 10 + stop 1 ! 프로그램을 중지하고 조건 코드 1을 반환합니다. +10 j = 201 ! 이 줄은 10번 줄로 레이블이 지정됩니다. + + ! 배열 + ! ====== + array = (/1, 2, 3, 4, 5, 6/) + array = [1, 2, 3, 4, 5, 6] ! Fortran 2003 표기법 사용. + arrayb = [10.2, 3e3, 0.41, 4e-5] + array2d = reshape([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [3, 2]) + + ! Fortran 배열 인덱싱은 1부터 시작합니다. + ! (기본적으로 특정 배열에 대해 다르게 정의할 수 있음). + v = array(1) ! 배열의 첫 번째 요소를 가져옵니다. + v = array2d(2, 2) + + print *, array(3:5) ! 3번째부터 5번째까지(포함) 모든 요소를 인쇄합니다. + print *, array2d(1, :) ! 2d 배열의 첫 번째 열을 인쇄합니다. + + array = array*3 + 2 ! 배열에 수학적 표현식을 적용할 수 있습니다. + array = array*array ! 배열 연산은 요소별로 발생합니다. + ! array = array*array2d ! 이러한 배열은 호환되지 않습니다. + + ! 배열에서 작동하는 많은 내장 함수가 있습니다. + c = dot_product(array, array) ! 이것은 내적입니다. + ! 행렬 수학에는 matmul()을 사용하십시오. + c = sum(array) + c = maxval(array) + print *, minloc(array) + c = size(array) + print *, shape(array) + m = count(array > 0) + + ! 배열 반복 (일반적으로 Product() 함수를 사용할 수 있음). + v = 1 + do i = 1, size(array) + v = v*array(i) + end do + + ! 조건부로 요소별 할당을 실행합니다. + array = [1, 2, 3, 4, 5, 6] + where (array > 3) + array = array + 1 + elsewhere(array == 2) + array = 1 + elsewhere + array = 0 + end where + + ! 암시적 DO 루프는 배열을 만드는 간결한 방법입니다. + array = [(i, i=1, 6)] ! [1,2,3,4,5,6] 배열을 만듭니다. + array = [(i, i=1, 12, 2)] ! [1,3,5,7,9,11] 배열을 만듭니다. + array = [(i**2, i=1, 6)] ! [1,4,9,16,25,36] 배열을 만듭니다. + array = [(4, 5, i=1, 3)] ! [4,5,4,5,4,5] 배열을 만듭니다. + + ! 입출력 + ! ============ + + print *, b ! 명령줄에 변수 'b'를 인쇄합니다. + + ! 인쇄된 출력을 서식 지정할 수 있습니다. + print "(I6)", 320 ! ' 320'을 인쇄합니다. + print "(I6.4)", 3 ! ' 0003'을 인쇄합니다. + print "(F6.3)", 4.32 ! ' 4.320'을 인쇄합니다. + + ! 문자는 예상 유형을 나타내고 그 뒤의 숫자는 + ! 값을 인쇄하는 데 사용할 문자 수를 나타냅니다. + ! 문자는 I(정수), F(실수), E(공학 형식), + ! L(논리), A(문자) 등이 될 수 있습니다... + print "(I3)", 3200 ! 숫자가 맞지 않으므로 '***'를 인쇄합니다. + + ! 여러 형식 사양을 가질 수 있습니다. + print "(I5,F6.2,E6.2)", 120, 43.41, 43.41 + + ! 3개의 정수 반복(필드 너비 = 5). + print "(3I5)", 10, 20, 30 + + ! 형식의 반복 그룹화. + print "(2(I5,F6.2))", 120, 43.42, 340, 65.3 + + ! 터미널에서 입력을 읽을 수도 있습니다. + read (*, *) v + read (*, "(2F6.2)") v, x ! 두 개의 숫자를 읽습니다. + + ! 파일을 쓰려면. + open (unit=12, file="records.txt", status="replace") + ! 파일은 9:99 범위에서 선택한 정수인 '단위 번호'로 참조됩니다. 상태는 {'old','replace','new'} 중 하나일 수 있습니다. + write (12, "(F10.2,F10.2,F10.2)") c, b, a + close (12) + + ! 파일을 읽으려면. + open (newunit=m, file="records.txt", status="old") + ! 파일은 컴파일러가 선택한 정수인 '새 단위 번호'로 참조됩니다. + + read (unit=m, fmt="(3F10.2)") a, b, c + close (m) + + ! 여기서 논의된 것보다 더 많은 기능이 있으며, 이전 Fortran 버전과의 이전 버전과의 호환성으로 인해 대체 변형이 있습니다. + + ! 내장 함수 + ! ================== + + ! Fortran에는 언어에 내장된 약 200개의 함수/서브루틴이 있습니다. + ! 예 - + call cpu_time(v) ! 'v'를 초 단위 시간으로 설정합니다. + k = ior(i, j) ! 2개의 정수에 대한 비트 OR입니다. + v = log10(x) ! 밑이 10인 로그입니다. + i = floor(b) ! b를 내림하여 정수로 변환합니다. + v = aimag(w) ! 복소수의 허수부입니다. + + ! 함수 및 서브루틴 + ! ======================= + + ! 서브루틴은 일부 입력 값에 대해 일부 코드를 실행하고 부작용을 일으키거나 입력 값을 수정할 수 있습니다. + + call routine(a, c, v) ! 서브루틴 호출입니다. + + ! 함수는 여러 입력 매개변수를 사용하고 단일 값을 반환합니다. + ! 그러나 입력 매개변수는 여전히 수정될 수 있으며 부작용이 실행될 수 있습니다. + + m = func(3, 2, k) ! 함수 호출입니다. + + ! 함수 호출은 표현식 내에서도 호출될 수 있습니다. + print *, func2(3, 2, k) + + ! 순수 함수는 입력 매개변수를 수정하거나 부작용을 일으키지 않는 함수입니다. + m = func3(3, 2, k) + +contains ! 프로그램의 내부 프로시저 정의 시작: + + ! Fortran에는 함수를 정의하는 약간 다른 방법이 몇 가지 있습니다. + + integer function func(a, b, c) ! 정수 값을 반환하는 함수입니다. + ! implicit none ! - 더 이상 하위 변수 필드에서 사용되지 않음 + integer, intent(in) :: a, b, c ! 입력 매개변수의 유형 + ! 반환 변수는 기본적으로 함수 이름입니다. + + if (a >= 2) then + func = a + b + c + return ! 'func'에서 현재 값을 반환합니다. + end if + func = a + c + + ! 함수 끝에 return 문이 필요하지 않습니다. + end function func + + function func2(a, b, c) result(f) ! 반환 변수가 'f'로 선언되었습니다. + integer, intent(in) :: a, b ! 변수가 함수에 의해 수정되지 않도록 선언하고 강제할 수 있습니다. + integer, intent(inout) :: c + integer :: f + ! 함수 반환 유형이 함수 내에서 선언되었습니다. + integer :: cnt = 0 ! GOTCHA - + ! 초기화 시 값을 할당하면 + ! 변수가 함수 호출 간에 저장됨을 의미합니다. + + f = a + b - c + c = 4 ! 입력 변수 c의 값 변경. + cnt = cnt + 1 ! 함수 호출 수 계산. + + end function func2 + + pure function func3(a, b, c) ! 순수 함수는 부작용이 없습니다. + integer, intent(in) :: a, b, c + integer :: func3 + + func3 = a*b*c + + end function func3 + + ! 서브루틴은 아무것도 반환하지 않지만, + ! 인수 값을 변경할 수 있습니다. + subroutine routine(d, e, f) + real, intent(inout) :: f + real, intent(in) :: d, e + + f = 2*d + 3*e + f + + end subroutine routine + +end program example +! 프로그램 정의 끝 ----------------------- + +! 프로그램 목록 외부에 선언된 함수 및 서브루틴은 인터페이스 선언을 사용하여 프로그램에 선언해야 합니다(동일한 소스 파일에 있더라도!). (아래 참조). 모듈 또는 프로그램의 'contains' 섹션 내에 정의하는 것이 더 쉽습니다. + +elemental real function func4(a) result(res) +! 요소 함수는 스칼라 입력 변수를 사용하는 순수 함수이지만, 배열에서도 사용할 수 있으며, 배열의 모든 요소에 개별적으로 적용되고 새 배열을 반환합니다. + real, intent(in) :: a + + res = a**2 + 1.0 + +end function func4 + +! 모듈 +! ======= + +! 모듈은 관련 선언, 함수 및 서브루틴을 함께 수집하여 재사용성을 높이는 유용한 방법입니다. + +module fruit + + real :: apple + real :: pear + real :: orange + +end module fruit + +module fruity + ! 선언은 모듈, 인터페이스, 변수 순서여야 합니다. + ! (프로그램에서도 모듈 및 인터페이스를 선언할 수 있음). + + use fruit, only: apple, pear ! fruit 모듈에서 apple 및 pear 사용. + implicit none ! 모듈 가져오기 뒤에 옴. + + ! 기본적으로 모든 모듈 데이터 및 함수는 public입니다. + private ! 대신 기본값을 private으로 설정 + ! 일부 변수/함수를 명시적으로 public으로 선언합니다. + public :: apple, mycar, create_mycar + ! 모듈에 일부 변수/함수를 private으로 선언합니다(여기서는 중복됨). + private :: func4 + + ! 인터페이스 + ! ========== + ! 모듈 내에서 외부 함수/프로시저를 명시적으로 선언합니다. + ! (일반적으로 'contains' 섹션에 함수/프로시저를 넣는 것이 더 좋음). + interface + elemental real function func4(a) result(res) + real, intent(in) :: a + end function func4 + end interface + + ! 오버로드된 함수는 명명된 인터페이스를 사용하여 정의할 수 있습니다. + interface myabs + ! 'module procedure' 키워드를 사용하여 모듈 내에 이미 정의된 함수를 포함할 수 있습니다. + module procedure real_abs, complex_abs + end interface + + ! 파생 데이터 유형 + ! ================== + ! 사용자 지정 구조화된 데이터 컬렉션을 만들 수 있습니다. + type car + character(len=100) :: model + real :: weight ! (kg) + real :: dimensions(3) ! 즉, 길이-너비-높이 (미터). + character :: colour + contains + procedure :: info ! 프로시저를 유형에 바인딩합니다. + end type car + + type(car) :: mycar ! 사용자 지정 유형의 변수를 선언합니다. + ! 사용법은 create_mycar() 루틴을 참조하십시오. + + ! 참고: 모듈에는 실행 가능한 문이 없습니다. + +contains + + subroutine create_mycar(mycar) + ! 파생 데이터 유형의 사용법을 보여줍니다. + type(car), intent(out) :: mycar + + ! '%' 연산자를 사용하여 유형 요소에 액세스합니다. + mycar%model = "Ford Prefect" + mycar%colour = 'r' + mycar%weight = 1400 + mycar%dimensions(1) = 5.0 ! 기본 인덱싱은 1부터 시작합니다! + mycar%dimensions(2) = 3.0 + mycar%dimensions(3) = 1.5 + + end subroutine create_mycar + + subroutine info(self) + class(car), intent(in) :: self + ! 'class' 키워드는 여기서 프로시저를 유형에 바인딩하는 데 사용됩니다. + + print *, "Model : ", self%model + print *, "Colour : ", self%colour + print *, "Weight : ", self%weight + print *, "Dimensions: ", self%dimensions + + end subroutine info + + real pure function real_abs(x) + real, intent(in) :: x + + if (x < 0) then + real_abs = -x + else + real_abs = x + end if + + end function real_abs + + real pure function complex_abs(z) + complex, intent(in) :: z + ! 긴 줄은 연속 문자 '&'를 사용하여 계속할 수 있습니다. + + complex_abs = sqrt(real(z)**2 + & + aimag(z)**2) + + end function complex_abs + +end module fruity + +! ISO 표준 Fortran 2008은 루프 수준 병렬 처리를 표현할 수 있도록 DO CONCURRENT 구문을 도입했습니다. + +integer :: i +real :: array(10) + +DO CONCURRENT (i = 1:size(array)) + array(i) = sqrt(real(i)**i) +END DO + + +! 루프 내에서는 순수 함수 호출만 허용되며 여러 인덱스를 선언할 수 있습니다: + +integer :: x, y +real :: array(8, 16) + +do concurrent (x = 1:size(array, 1), y = 1:size(array, 2)) + array(x, y) = real(x) +end do + +! 루프 인덱스는 구문 내에서도 선언할 수 있습니다: + +real :: array(8, 16) + +do concurrent (integer :: x = 1:size(array, 1), y = 1:size(array, 2)) + array(x, y) = real(x) +end do +``` + +### 추가 자료 + +Fortran에 대한 자세한 내용은 다음을 참조하십시오: + ++ [위키백과](https://en.wikipedia.org/wiki/Fortran) ++ [Fortran-lang 조직](https://fortran-lang.org/) ++ [Fortran_95_language_features](https://en.wikipedia.org/wiki/Fortran_95_language_features) ++ [fortranwiki.org](http://fortranwiki.org) ++ [www.fortran90.org/](http://www.fortran90.org) ++ [Fortran 95 튜토리얼 목록](http://www.dmoz.org/Computers/Programming/Languages/Fortran/FAQs%2C_Help%2C_and_Tutorials/Fortran_90_and_95/) ++ [Fortran 위키북](https://en.wikibooks.org/wiki/Fortran) ++ [Fortran 자료](http://www.fortranplus.co.uk/resources/fortran_resources.pdf) ++ [놀랄 수 있는 Fortran 90 프로그램의 실수](http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html) \ No newline at end of file diff --git a/ko/fsharp.md b/ko/fsharp.md new file mode 100644 index 0000000000..1bda677e78 --- /dev/null +++ b/ko/fsharp.md @@ -0,0 +1,642 @@ +--- +name: F# +contributors: + - ["Scott Wlaschin", "http://fsharpforfunandprofit.com/"] +filename: learnfsharp.fs +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +F#은 범용 함수형/객체 지향 프로그래밍 언어입니다. 무료이며 오픈 소스이며 Linux, Mac, Windows 등에서 실행됩니다. + +컴파일 타임에 많은 오류를 포착하는 강력한 타입 시스템을 가지고 있지만, 동적 언어처럼 읽히도록 타입 추론을 사용합니다. + +F#의 구문은 C 스타일 언어와 다릅니다: + +* 중괄호는 코드 블록을 구분하는 데 사용되지 않습니다. 대신 들여쓰기가 사용됩니다(Python과 유사). +* 매개변수를 구분하는 데 쉼표 대신 공백이 사용됩니다. + +아래 코드를 사용해 보려면 [https://try.fsharp.org](https://try.fsharp.org)로 이동하여 대화형 REPL에 붙여넣을 수 있습니다. + +```fsharp +// 한 줄 주석은 이중 슬래시를 사용합니다. +(* 여러 줄 주석은 (* . . . *) 쌍을 사용합니다. + +- 여러 줄 주석 끝 - *) + +// ================================================ +// 기본 구문 +// ================================================ + +// ------ "변수" (하지만 실제로는 아님) ------ +// "let" 키워드는 (불변) 값을 정의합니다. +let myInt = 5 +let myFloat = 3.14 +let myString = "hello" // 타입이 필요하지 않음에 유의하십시오. + +// 변경 가능한 변수 +let mutable a=3 +a <- 4 // a는 이제 4입니다. + +// 다소 변경 가능한 변수 +// 참조 셀은 참조 의미 체계를 사용하여 변경 가능한 값을 만들 수 있는 저장 위치입니다. +// https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/reference-cells 참조 +let xRef = ref 10 +printfn "%d" xRef.Value // 10 +xRef.Value <- 11 +printfn "%d" xRef.Value // 11 + +let a=[ref 0; ref 1] // 다소 변경 가능한 목록 +a[0].Value <- 2 + +// ------ 목록 ------ +let twoToFive = [2; 3; 4; 5] // 대괄호는 세미콜론 구분 기호가 있는 목록을 만듭니다. +let oneToFive = 1 :: twoToFive // ::는 새 첫 번째 요소가 있는 목록을 만듭니다. +// 결과는 [1; 2; 3; 4; 5]입니다. +let zeroToFive = [0; 1] @ twoToFive // @는 두 목록을 연결합니다. + +// 중요: 쉼표는 구분 기호로 절대 사용되지 않으며 세미콜론만 사용됩니다! + +// ------ 함수 ------ +// "let" 키워드는 명명된 함수도 정의합니다. +let square x = x * x // 괄호가 사용되지 않음에 유의하십시오. +square 3 // 이제 함수를 실행합니다. 다시 말하지만, 괄호가 없습니다. + +let add x y = x + y // add (x,y)를 사용하지 마십시오! 완전히 다른 의미입니다. +add 2 3 // 이제 함수를 실행합니다. + +// 여러 줄 함수를 정의하려면 들여쓰기를 사용하십시오. 세미콜론이 필요하지 않습니다. +let evens list = + let isEven x = x % 2 = 0 // "isEven"을 하위 함수로 정의합니다. 등호 연산자는 단일 문자 "="입니다. + List.filter isEven list // List.filter는 라이브러리 함수입니다. + // 두 개의 매개변수: 부울 함수와 작업할 목록 + +evens oneToFive // 이제 함수를 실행합니다. + +// 우선 순위를 명확히 하기 위해 괄호를 사용할 수 있습니다. 이 예제에서는 +// 먼저 두 개의 인수로 "map"을 수행한 다음 결과에 대해 "sum"을 수행합니다. +// 괄호가 없으면 "List.map"이 List.sum에 인수로 전달됩니다. +let sumOfSquaresTo100 = + List.sum ( List.map square [1..100] ) + +// "|>"를 사용하여 한 작업의 출력을 다음 작업으로 파이프할 수 있습니다. +// 데이터를 파이핑하는 것은 UNIX 파이프와 유사하게 F#에서 매우 일반적입니다. + +// 다음은 파이프를 사용하여 작성된 동일한 sumOfSquares 함수입니다. +let sumOfSquaresTo100piped = + [1..100] |> List.map square |> List.sum // "square"는 이전에 정의되었습니다. + +// "fun" 키워드를 사용하여 람다(익명 함수)를 정의할 수 있습니다. +let sumOfSquaresTo100withFun = + [1..100] |> List.map (fun x -> x * x) |> List.sum + +// F#에는 "return" 키워드가 없습니다. 함수는 항상 +// 사용된 마지막 표현식의 값을 반환합니다. + +// ------ 패턴 매칭 ------ +// Match..with..는 매우 강력한 case/switch 문입니다. +let simplePatternMatch = + let x = "a" + match x with + | "a" -> printfn "x is a" + | "b" -> printfn "x is b" + | _ -> printfn "x is something else" // 밑줄은 무엇이든 일치합니다. + +// F#은 기본적으로 null을 허용하지 않습니다 -- Option 타입을 사용해야 합니다. +// 그런 다음 패턴 매칭을 합니다. +// Some(..) 및 None은 Nullable 래퍼와 거의 유사합니다. +let validValue = Some(99) +let invalidValue = None + +// 이 예제에서 match..with는 "Some"과 "None"을 일치시키고, +// 동시에 "Some"의 값을 풉니다. +let optionPatternMatch input = + match input with + | Some i -> printfn "input is an int=%d" i + | None -> printfn "input is missing" + +optionPatternMatch validValue +optionPatternMatch invalidValue + +// ------ 인쇄 ------ +// printf/printfn 함수는 C#의 +// Console.Write/WriteLine 함수와 유사합니다. +printfn "Printing an int %i, a float %f, a bool %b" 1 2.0 true +printfn "A string %s, and something generic %A" "hello" [1; 2; 3; 4] + +// 데이터를 문자열로 서식 지정하기 위한 sprintf/sprintfn 함수도 있습니다. +// C#의 String.Format과 유사합니다. + +// ================================================ +// 함수에 대한 추가 정보 +// ================================================ + +// F#은 진정한 함수형 언어입니다 -- 함수는 일급 +// 엔티티이며 강력한 구문을 만들기 위해 쉽게 결합할 수 있습니다. + +// 모듈은 함수를 함께 그룹화하는 데 사용됩니다. +// 각 중첩 모듈에 대해 들여쓰기가 필요합니다. +module FunctionExamples = + + // 간단한 덧셈 함수 정의 + let add x y = x + y + + // 함수의 기본 사용법 + let a = add 1 2 + printfn "1 + 2 = %i" a + + // 매개변수를 "굽기" 위한 부분 적용 + let add42 = add 42 + let b = add42 1 + printfn "42 + 1 = %i" b + + // 함수를 결합하기 위한 구성 + let add1 = add 1 + let add2 = add 2 + let add3 = add1 >> add2 + let c = add3 7 + printfn "3 + 7 = %i" c + + // 고차 함수 + [1..10] |> List.map add3 |> printfn "new list is %A" + + // 함수 목록 등 + let add6 = [add1; add2; add3] |> List.reduce (>>) + let d = add6 7 + printfn "1 + 2 + 3 + 7 = %i" d + +// ================================================ +// 목록 및 컬렉션 +// ================================================ + +// 세 가지 유형의 정렬된 컬렉션이 있습니다: +// * 목록은 가장 기본적인 불변 컬렉션입니다. +// * 배열은 변경 가능하며 필요할 때 더 효율적입니다. +// * 시퀀스는 지연되고 무한합니다(예: 열거자). +// +// 다른 컬렉션에는 불변 맵 및 집합이 포함됩니다. +// بالإضافة إلى جميع مجموعات .NET القياسية + +module ListExamples = + + // 목록은 대괄호를 사용합니다. + let list1 = ["a"; "b"] + let list2 = "c" :: list1 // ::는 앞에 추가하는 것입니다. + let list3 = list1 @ list2 // @는 연결입니다. + + // 목록 이해(생성기라고도 함) + let squares = [for i in 1..10 do yield i * i] + + // 소수 생성기 + // - 이것은 패턴 매칭 구문에 대한 짧은 표기법을 사용합니다. + // - (p::xs)는 목록의 '첫 번째 :: 꼬리'이며, p :: xs로도 작성할 수 있습니다. + // 이것은 'p'(목록의 첫 번째 항목)와 일치하고 xs는 목록의 나머지 부분임을 의미합니다. + // 이것을 'cons 패턴'이라고 합니다. + // - 재귀를 사용할 때 필요한 'rec' 키워드를 사용합니다. + let rec sieve = function + | (p::xs) -> p :: sieve [ for x in xs do if x % p > 0 then yield x ] + | [] -> [] + let primes = sieve [2..50] + printfn "%A" primes + + // 목록에 대한 패턴 매칭 + let listMatcher aList = + match aList with + | [] -> printfn "the list is empty" + | [first] -> printfn "the list has one element %A " first + | [first; second] -> printfn "list is %A and %A" first second + | first :: _ -> printfn "the list has more than two elements, first element %A" first + + listMatcher [1; 2; 3; 4] + listMatcher [1; 2] + listMatcher [1] + listMatcher [] + + // 목록을 사용한 재귀 + let rec sum aList = + match aList with + | [] -> 0 + | x::xs -> x + sum xs + sum [1..10] + + // ----------------------------------------- + // 표준 라이브러리 함수 + // ----------------------------------------- + + // map + let add3 x = x + 3 + [1..10] |> List.map add3 + + // filter + let even x = x % 2 = 0 + [1..10] |> List.filter even + + // 더 많음 -- 문서 참조 + +module ArrayExamples = + + // 배열은 대괄호와 막대를 사용합니다. + let array1 = [| "a"; "b" |] + let first = array1.[0] // 점을 사용한 인덱스 액세스 + + // 배열에 대한 패턴 매칭은 목록과 동일합니다. + let arrayMatcher aList = + match aList with + | [| |] -> printfn "the array is empty" + | [| first |] -> printfn "the array has one element %A " first + | [| first; second |] -> printfn "array is %A and %A" first second + | _ -> printfn "the array has more than two elements" + + arrayMatcher [| 1; 2; 3; 4 |] + + // 목록과 마찬가지로 표준 라이브러리 함수 + + [| 1..10 |] + |> Array.map (fun i -> i + 3) + |> Array.filter (fun i -> i % 2 = 0) + |> Array.iter (printfn "value is %i. ") + + +module SequenceExamples = + + // 시퀀스는 중괄호를 사용합니다. + let seq1 = seq { yield "a"; yield "b" } + + // 시퀀스는 yield를 사용할 수 있으며 + // 하위 시퀀스를 포함할 수 있습니다. + let strange = seq { + // "yield"는 한 요소를 추가합니다. + yield 1; yield 2; + + // "yield!"는 전체 하위 시퀀스를 추가합니다. + yield! [5..10] + yield! seq { + for i in 1..10 do + if i % 2 = 0 then yield i }} + // test + strange |> Seq.toList + + + // 시퀀스는 "unfold"를 사용하여 만들 수 있습니다. + // 다음은 피보나치 수열입니다. + let fib = Seq.unfold (fun (fst,snd) -> + Some(fst + snd, (snd, fst + snd))) (0,1) + + // test + let fib10 = fib |> Seq.take 10 |> Seq.toList + printf "first 10 fibs are %A" fib10 + + +// ================================================ +// 데이터 유형 +// ================================================ + +module DataTypeExamples = + + // 모든 데이터는 기본적으로 불변입니다. + + // 튜플은 빠른 'n 쉬운 익명 유형입니다. + // -- 쉼표를 사용하여 튜플을 만듭니다. + let twoTuple = 1, 2 + let threeTuple = "a", 2, true + + // 패턴 매칭하여 풀기 + let x, y = twoTuple // x = 1, y = 2로 설정 + + // ------------------------------------ + // 레코드 유형에는 명명된 필드가 있습니다. + // ------------------------------------ + + // 중괄호가 있는 "type"을 사용하여 레코드 유형을 정의합니다. + type Person = {First:string; Last:string} + + // 중괄호가 있는 "let"을 사용하여 레코드를 만듭니다. + let person1 = {First="John"; Last="Doe"} + + // 패턴 매칭하여 풀기 + let {First = first} = person1 // first="John"으로 설정 + + // ------------------------------------ + // 공용체 유형(변형이라고도 함)에는 선택 집합이 있습니다. + // 한 번에 하나의 경우만 유효할 수 있습니다. + // ------------------------------------ + + // 막대/파이프가 있는 "type"을 사용하여 공용체 유형을 정의합니다. + type Temp = + | DegreesC of float + | DegreesF of float + + // 경우 중 하나를 사용하여 하나를 만듭니다. + let temp1 = DegreesF 98.6 + let temp2 = DegreesC 37.0 + + // 모든 경우에 대해 패턴 매칭하여 풀기 + let printTemp = function + | DegreesC t -> printfn "%f degC" t + | DegreesF t -> printfn "%f degF" t + + printTemp temp1 + printTemp temp2 + + // ------------------------------------ + // 재귀 유형 + // ------------------------------------ + + // 유형은 하위 클래스를 만들지 않고도 복잡한 방식으로 재귀적으로 결합할 수 있습니다. + type Employee = + | Worker of Person + | Manager of Employee list + + let jdoe = {First="John"; Last="Doe"} + let worker = Worker jdoe + + // ------------------------------------ + // 유형을 사용한 모델링 + // ------------------------------------ + + // 공용체 유형은 플래그를 사용하지 않고 상태를 모델링하는 데 적합합니다. + type EmailAddress = + | ValidEmailAddress of string + | InvalidEmailAddress of string + + let trySendEmail email = + match email with // 패턴 매칭 사용 + | ValidEmailAddress address -> () // 보내기 + | InvalidEmailAddress address -> () // 보내지 않기 + + // 공용체 유형과 레코드 유형의 조합은 + // 도메인 기반 설계를 위한 훌륭한 기반을 제공합니다. + // 도메인을 정확하게 반영하는 수백 개의 작은 유형을 만들 수 있습니다. + + type CartItem = { ProductCode: string; Qty: int } + type Payment = Payment of float + type ActiveCartData = { UnpaidItems: CartItem list } + type PaidCartData = { PaidItems: CartItem list; Payment: Payment} + + type ShoppingCart = + | EmptyCart // 데이터 없음 + | ActiveCart of ActiveCartData + | PaidCart of PaidCartData + + // ------------------------------------ + // 유형에 대한 내장 동작 + // ------------------------------------ + + // 핵심 유형에는 코딩이 필요 없는 유용한 "기본" 동작이 있습니다. + // * 불변성 + // * 디버깅 시 예쁜 인쇄 + // * 같음 및 비교 + // * 직렬화 + + // %A를 사용한 예쁜 인쇄 + printfn "twoTuple=%A,\nPerson=%A,\nTemp=%A,\nEmployee=%A" + twoTuple person1 temp1 worker + + // 내장된 같음 및 비교. + // 다음은 카드 예제입니다. + type Suit = Club | Diamond | Spade | Heart + type Rank = Two | Three | Four | Five | Six | Seven | Eight + | Nine | Ten | Jack | Queen | King | Ace + + let hand = [ Club, Ace; Heart, Three; Heart, Ace; + Spade, Jack; Diamond, Two; Diamond, Ace ] + + // 정렬 + List.sort hand |> printfn "sorted hand is (low to high) %A" + List.max hand |> printfn "high card is %A" + List.min hand |> printfn "low card is %A" + + +// ================================================ +// 활성 패턴 +// ================================================ + +module ActivePatternExamples = + + // F#에는 패턴이 동적으로 구문 분석되거나 감지될 수 있는 "활성 패턴"이라는 특수 유형의 패턴 매칭이 있습니다. + + // "바나나 클립"은 활성 패턴의 구문입니다. + + // 조건식에서 "else if" 대신 "elif"를 사용할 수 있습니다. + // F#에서는 동일합니다. + + // 예를 들어, 문자 유형과 일치하는 "활성" 패턴을 정의합니다... + let (|Digit|Letter|Whitespace|Other|) ch = + if System.Char.IsDigit(ch) then Digit + elif System.Char.IsLetter(ch) then Letter + elif System.Char.IsWhiteSpace(ch) then Whitespace + else Other + + // ... 그런 다음 구문 분석 논리를 훨씬 더 명확하게 만들기 위해 사용합니다. + let printChar ch = + match ch with + | Digit -> printfn "%c is a Digit" ch + | Letter -> printfn "%c is a Letter" ch + | Whitespace -> printfn "%c is a Whitespace" ch + | _ -> printfn "%c is something else" ch + + // 목록 인쇄 + ['a'; 'b'; '1'; ' '; '-'; 'c'] |> List.iter printChar + + // ----------------------------------- + // 활성 패턴을 사용한 FizzBuzz + // ----------------------------------- + + // 부분 일치 패턴도 만들 수 있습니다. + // 정의에서 밑줄을 사용하고 일치하면 Some을 반환하십시오. + let (|MultOf3|_|) i = if i % 3 = 0 then Some MultOf3 else None + let (|MultOf5|_|) i = if i % 5 = 0 then Some MultOf5 else None + + // 주 함수 + let fizzBuzz i = + match i with + | MultOf3 & MultOf5 -> printf "FizzBuzz, " + | MultOf3 -> printf "Fizz, " + | MultOf5 -> printf "Buzz, " + | _ -> printf "%i, " i + + // test + [1..20] |> List.iter fizzBuzz + +// ================================================ +// 간결함 +// ================================================ + +module AlgorithmExamples = + + // F#은 신호/잡음비가 높으므로 코드는 + // 실제 알고리즘과 거의 같습니다. + + // ------ 예제: sumOfSquares 함수 정의 ------ + let sumOfSquares n = + [1..n] // 1) 1에서 n까지의 모든 숫자를 가져옵니다. + |> List.map square // 2) 각각을 제곱합니다. + |> List.sum // 3) 결과를 합산합니다. + + // test + sumOfSquares 100 |> printfn "Sum of squares = %A" + + // ------ 예제: 정렬 함수 정의 ------ + let rec sort list = + match list with + // 목록이 비어 있으면 + | [] -> + [] // 빈 목록을 반환합니다. + // 목록이 비어 있지 않으면 + | firstElem::otherElements -> // 첫 번째 요소를 가져옵니다. + let smallerElements = // 더 작은 요소를 추출합니다. + otherElements // 나머지 요소에서 + |> List.filter (fun e -> e < firstElem) + |> sort // 그리고 정렬합니다. + let largerElements = // 더 큰 요소를 추출합니다. + otherElements // 나머지 요소에서 + |> List.filter (fun e -> e >= firstElem) + |> sort // 그리고 정렬합니다. + // 3개 부분을 새 목록으로 결합하고 반환합니다. + List.concat [smallerElements; [firstElem]; largerElements] + + // test + sort [1; 5; 23; 18; 9; 1; 3] |> printfn "Sorted = %A" + +// ================================================ +// 비동기 코드 +// ================================================ + +module AsyncExample = + + // F#에는 "파멸의 피라미드"를 만나지 않고도 비동기 코드를 지원하는 내장 기능이 있습니다. + // + // 다음 예제는 웹 페이지 집합을 병렬로 다운로드합니다. + + open System.Net + open System + open System.IO + open Microsoft.FSharp.Control.CommonExtensions + + // URL의 내용을 비동기적으로 가져옵니다. + let fetchUrlAsync url = + async { // "async" 키워드 및 중괄호 + // "async" 개체를 만듭니다. + let req = WebRequest.Create(Uri(url)) + use! resp = req.AsyncGetResponse() + // use!는 비동기 할당입니다. + use stream = resp.GetResponseStream() + // "use"는 범위 끝에서 리소스에 대해 자동 close()를 트리거합니다. + use reader = new IO.StreamReader(stream) + let html = reader.ReadToEnd() + printfn "finished downloading %s" url + } + + // 가져올 사이트 목록 + let sites = ["http://www.bing.com"; + "http://www.google.com"; + "http://www.microsoft.com"; + "http://www.amazon.com"; + "http://www.yahoo.com"] + + // do it + sites + |> List.map fetchUrlAsync // 비동기 작업 목록 만들기 + |> Async.Parallel // 병렬로 실행할 작업 설정 + |> Async.RunSynchronously // 시작 + +// ================================================ +// .NET 호환성 +// ================================================ + +module NetCompatibilityExamples = + + // F#은 C#이 할 수 있는 거의 모든 것을 할 수 있으며, .NET 또는 Mono 라이브러리와 원활하게 통합됩니다. + + // ------- 기존 라이브러리 함수 작업 ------- + + let (i1success, i1) = System.Int32.TryParse("123"); + if i1success then printfn "parsed as %i" i1 else printfn "parse failed" + + // ------- 즉시 인터페이스 구현! ------- + + // IDisposable을 구현하는 새 개체 만들기 + let makeResource name = + { new System.IDisposable + with member this.Dispose() = printfn "%s disposed" name } + + let useAndDisposeResources = + use r1 = makeResource "first resource" + printfn "using first resource" + for i in [1..3] do + let resourceName = sprintf "\tinner resource %d" i + use temp = makeResource resourceName + printfn "\tdo something with %s" resourceName + use r2 = makeResource "second resource" + printfn "using second resource" + printfn "done." + + // ------- 객체 지향 코드 ------- + + // F#은 또한 완전한 OO 언어입니다. + // 클래스, 상속, 가상 메서드 등을 지원합니다. + + // 제네릭 유형이 있는 인터페이스 + type IEnumerator<'a> = + abstract member Current : 'a + abstract MoveNext : unit -> bool + + // 가상 메서드가 있는 추상 기본 클래스 + [] + type Shape() = + // 읽기 전용 속성 + abstract member Width : int with get + abstract member Height : int with get + // 비가상 메서드 + member this.BoundingArea = this.Height * this.Width + // 기본 구현이 있는 가상 메서드 + abstract member Print : unit -> unit + default this.Print () = printfn "I'm a shape" + + // 기본 클래스에서 상속하고 재정의하는 구체적인 클래스 + type Rectangle(x:int, y:int) = + inherit Shape() + override this.Width = x + override this.Height = y + override this.Print () = printfn "I'm a Rectangle" + + // test + let r = Rectangle(2, 3) + printfn "The width is %i" r.Width + printfn "The area is %i" r.BoundingArea + r.Print() + + // ------- 확장 메서드 ------- + + // C#과 마찬가지로 F#은 확장 메서드로 기존 클래스를 확장할 수 있습니다. + type System.String with + member this.StartsWithA = this.StartsWith "A" + + // test + let s = "Alice" + printfn "'%s' starts with an 'A' = %A" s s.StartsWithA + + // ------- 이벤트 ------- + + type MyButton() = + let clickEvent = new Event<_>() + + [] + member this.OnClick = clickEvent.Publish + + member this.TestEvent(arg) = + clickEvent.Trigger(this, arg) + + // test + let myButton = new MyButton() + myButton.OnClick.Add(fun (sender, arg) -> + printfn "Click event with arg=%O" arg) + + myButton.TestEvent("Hello World!") +``` + +## 추가 정보 + +F#에 대한 더 많은 데모를 보려면 [왜 F#을 사용해야 하는가](http://fsharpforfunandprofit.com/why-use-fsharp/) 시리즈를 참조하십시오. + +[fsharp.org](http://fsharp.org/) 및 [dotnet의 F# 페이지](https://dotnet.microsoft.com/languages/fsharp)에서 F#에 대해 자세히 알아보십시오. \ No newline at end of file diff --git a/ko/gdscript.md b/ko/gdscript.md new file mode 100644 index 0000000000..3df36f25e8 --- /dev/null +++ b/ko/gdscript.md @@ -0,0 +1,366 @@ +--- +name: GDScript +contributors: + - ["Wichamir", "https://github.com/Wichamir/"] + - ["zacryol", "https://github.com/zacryol"] +filename: learngdscript.gd +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +GDScript는 무료 오픈 소스 게임 엔진 Godot를 위한 동적 및 정적 유형 스크립팅 언어입니다. 구문은 Python과 막연하게 유사합니다. 주요 장점은 사용 편의성과 엔진과의 긴밀한 통합입니다. 게임 개발에 완벽하게 적합합니다. + +## 기본 + +```gdscript +# 한 줄 주석은 해시 기호를 사용하여 작성됩니다. +""" + 여러 줄 + 주석은 + 삼중 + 따옴표 + 문자열을 + 사용하여 + 작성됩니다. +""" + +# 문서 주석은 클래스 및 필드에 설명을 추가할 수 있으며 +# 엔진 내 문서에서 볼 수 있습니다. + +## 이 클래스는 GDScript의 데모입니다. + +# 스크립트 파일은 그 자체로 클래스이며 선택적으로 이름을 정의할 수 있습니다. +class_name MyClass + +# 상속 +extends Node2D + +# 멤버 변수 +var x = 8 # int +var y = 1.2 # float +var b = true # bool +var s = "Hello World!" # String +var a = [1, false, "brown fox"] # Array - Python의 목록과 유사하며, + # 한 번에 다른 유형의 변수를 + # 보유할 수 있습니다. +var d = { + "key" : "value", + 42 : true +} # Dictionary는 키-값 쌍을 보유합니다. +var p_arr = PackedStringArray(["Hi", "there", "!"]) # Packed Arrays는 + # 특정 유형만 보유할 수 있습니다. + +# 문서 주석은 속성에 적용할 수 있습니다. + +## 이 개체가 점프한 횟수 +var jump_count = 0 + +# 내장 벡터 유형: +var v2 = Vector2(1, 2) +var v3 = Vector3(1, 2, 3) + +# 상수 +const ANSWER_TO_EVERYTHING = 42 +const BREAKFAST = "Spam and eggs!" + +# 열거형 +enum { ZERO, ONE , TWO, THREE } +enum NamedEnum { ONE = 1, TWO, THREE } + +# 내보낸 변수는 검사기에서 볼 수 있습니다. +# +# 편집기가 어떤 옵션을 제공해야 하는지 알기 위해서는 +# 유형 힌트(나중에 설명) 또는 기본값이 필요합니다. +@export var age: int +@export var height: float +@export var person_name = "Bob" +# 하지만 둘 다 허용됩니다. +@export var favorite_color: String = "Green" +@export var favorite_food := "Pizza" + +# 함수 +func foo(): + pass # pass 키워드는 향후 코드를 위한 자리 표시자입니다. + +func add(first, second): + return first + second + +# 함수에 대한 문서 주석 + +## 점프 횟수 증가 +func jump(): + jump_count += 1 + +# 값 인쇄 +func printing(): + print("GDScript ", "is ", " awesome.") + prints("These", "words", "are", "divided", "by", "spaces.") + printt("These", "words", "are", "divided", "by", "tabs.") + printraw("This gets printed to system console.") + + # 람다 + var my_lambda = func(): print("hello from lambda!") + + my_lambda.call() + +# 수학 +func doing_math(): + var first = 8 + var second = 4 + print(first + second) # 12 + print(first - second) # 4 + print(first * second) # 32 + print(first / second) # 2 + print(first % second) # 0 + # +=, -=, *=, /=, %= 등도 있지만, + # ++ 또는 -- 연산자는 없습니다. + print(pow(first, 2)) # 64 + print(sqrt(second)) # 2 + printt(PI, TAU, INF, NAN) # 내장 상수 + +# 제어 흐름 +func control_flow(): + x = 8 + y = 2 # y는 원래 float였지만, + # 동적 타이핑의 힘을 사용하여 + # 유형을 int로 변경할 수 있습니다! + + if x < y: + print("x is smaller than y") + elif x > y: + print("x is bigger than y") + else: + print("x and y are equal") + + var a = true + var b = false + var c = false + if a and b or not c: # 또는 &&, || 및 !를 사용할 수 있습니다. + print("This is true!") + + for i in range(20): # GDScript의 범위는 Python과 유사합니다. + print(i) # 따라서 이것은 0에서 19까지의 숫자를 인쇄합니다. + + for i in 20: # Python과 달리 int를 직접 반복할 수 있습니다. + print(i) # 따라서 이것은 0에서 19까지의 숫자를 인쇄합니다. + + for i in ["two", 3, 1.0]: # 배열 반복 + print(i) + + while x > y: + printt(x, y) + y += 1 + + x = 2 + y = 10 + while x < y: + x += 1 + if x == 6: + continue # 6은 continue 문 때문에 인쇄되지 않습니다. + prints("x is equal to:", x) + if x == 7: + break # 루프는 7에서 중단되므로 8, 9, 10은 인쇄되지 않습니다. + + match x: + 1: + print("Match is similar to switch.") + 2: + print("However you don't need to put cases before each value.") + 3: + print("Furthermore each case breaks on default.") + break # 오류! Break 문은 불필요합니다! + 4: + print("If you need fallthrough use continue.") + continue + _: + print("Underscore is a default case.") + + # 삼항 연산자 (한 줄 if-else 문) + prints("x is", "positive" if x >= 0 else "negative") + +# 캐스팅 +func casting_examples(): + var i = 42 + var f = float(42) # 변수 생성자를 사용하여 캐스팅 + var b = i as bool # 또는 "as" 키워드를 사용하여 + +# 재정의 함수 +# 규칙에 따라 내장 재정의 가능 함수는 밑줄로 시작하지만, +# 실제로는 거의 모든 함수를 재정의할 수 있습니다. + +# _init은 개체가 초기화될 때 호출됩니다. +# 이것은 개체의 생성자입니다. +func _init(): + # 여기에서 개체의 내부 항목을 초기화합니다. + pass + +# _ready는 스크립트의 노드와 +# 해당 자식이 장면 트리에 들어갈 때 호출됩니다. +func _ready(): + pass + +# _process는 모든 프레임에서 호출됩니다. +func _process(delta): + # 이 함수에 전달된 델타 인수는 초 단위의 숫자이며, + # 마지막 프레임과 현재 프레임 사이에 경과된 시간입니다. + print("Delta time equals: ", delta) + +# _physics_process는 모든 물리 프레임에서 호출됩니다. +# 즉, 델타는 일정해야 합니다. +func _physics_process(delta): + # 벡터 덧셈 및 곱셈을 사용한 간단한 이동. + var direction = Vector2(1, 0) # 또는 Vector2.RIGHT + var speed = 100.0 + self.global_position += direction * speed * delta + # self는 현재 클래스 인스턴스를 참조합니다. + +# 재정의할 때 점 연산자를 사용하여 부모의 함수를 호출할 수 있습니다. +# 여기처럼: +func get_children(): + # 여기에서 추가 작업을 수행합니다. + var r = super() # 부모의 구현 호출 + return r + +# 내부 클래스 +class InnerClass: + extends Object + + func hello(): + print("Hello from inner class!") + +func use_inner_class(): + var ic = InnerClass.new() + ic.hello() + ic.free() # 메모리 정리를 위해 free 사용 +``` + +## 장면 트리의 다른 노드에 액세스 + +```gdscript +extends Node2D + +var sprite # 이 변수는 참조를 보유합니다. + +# _ready에서 다른 노드에 대한 참조를 얻을 수 있습니다. +func _ready() -> void: + # NodePath는 노드에 액세스하는 데 유용합니다. + # 생성자에 문자열을 전달하여 NodePath를 만듭니다: + var path1 = NodePath("path/to/something") + # 또는 NodePath 리터럴을 사용합니다: + var path2 = ^"path/to/something" + # NodePath 예제: + var path3 = ^"Sprite" # 상대 경로, 현재 노드의 직계 자식 + var path4 = ^"Timers/Firerate" # 상대 경로, 자식의 자식 + var path5 = ^".." # 현재 노드의 부모 + var path6 = ^"../Enemy" # 현재 노드의 형제 + var path7 = ^"/root" # 절대 경로, get_tree().get_root()와 동일 + var path8 = ^"/root/Main/Player/Sprite" # 플레이어의 스프라이트에 대한 절대 경로 + var path9 = ^"Timers/Firerate:wait_time" # 속성 액세스 + var path10 = ^"Player:position:x" # 하위 속성 액세스 + + # 마지막으로 참조를 얻으려면 다음 중 하나를 사용하십시오: + sprite = get_node(^"Sprite") as Sprite # 항상 예상하는 유형으로 캐스팅 + sprite = get_node("Sprite") as Sprite # 여기서 문자열은 + # 암시적으로 NodePath로 캐스팅됩니다. + sprite = get_node(path3) as Sprite + sprite = get_node_or_null("Sprite") as Sprite + sprite = $Sprite as Sprite + +func _process(delta): + # 이제 다른 곳에서 참조를 재사용할 수 있습니다. + prints("Sprite has global_position of", sprite.global_position) + +# @onready 주석을 사용하여 +# _ready가 실행되기 직전에 변수에 값을 할당합니다. +# 이것은 일반적으로 사용되는 구문 설탕입니다. +@onready var other_sprite = $Sprite as Sprite + +# NodePath를 내보낼 수 있으므로 검사기 내에서 할당할 수 있습니다. +@export var nodepath = ^"" +@onready var reference = get_node(nodepath) as Node + +# 또는 노드를 직접 내보냅니다. +@export var other_reference: Node +``` + +## 신호 + +신호 시스템은 Godot의 관찰자 프로그래밍 패턴 구현입니다. 다음은 예입니다: + +```gdscript +class_name Player extends Node2D + +var hp = 10 + +# 문서 주석은 신호에도 적용될 수 있습니다. + +## 플레이어가 죽을 때 발생 +signal died() # 신호 정의 +signal hurt(hp_old, hp_new) # 신호는 인수를 가질 수 있습니다. + +func apply_damage(dmg): + var hp_old = hp + hp -= dmg + hurt.emit(hp_old, hp) # 신호를 발생시키고 인수 전달 + if hp <= 0: + died.emit() + +func _ready(): + # 신호 "died"를 self에 정의된 함수 "_on_death"에 연결 + died.connect(_on_death) + # 대체 방법 + # 대상 개체가 self가 아닌 경우 필요 + # died.connect(Callable(self, &"_on_death")) + +func _on_death(): + queue_free() # 죽을 때 플레이어 파괴 +``` + +## 유형 힌트 + +GDScript는 코드 명확성과 성능 이점을 위해 선택적으로 정적 타이핑을 사용할 수 있습니다. + +```gdscript +extends Node + +var x: int # 유형이 지정된 변수 정의 +var y: float = 4.2 +var z := 1.0 # := 연산자를 사용하여 기본값을 기반으로 유형 추론 + +var a: Array[int] = [1, 2, 3] # 배열은 유형 콘텐츠도 지정할 수 있습니다. + +enum NamedEnum { ONE = 1, TWO, THREE } +var n: NamedEnum = NamedEnum.ONE # 열거형은 유형으로도 사용할 수 있습니다. + +@onready var node_ref_typed := $Child as Node + +@export var speed := 50.0 + +const CONSTANT := "Typed constant." + +signal example(arg: int) + +func _ready() -> void: + # 함수는 아무것도 반환하지 않습니다. + x = "string" # 오류! 유형을 변경할 수 없습니다! + a.append("q") # 오류! Array[int]는 문자열을 보유할 수 없습니다! + return + +func join(arg1: String, arg2: String) -> String: + # 함수는 두 개의 문자열을 사용하고 문자열을 반환합니다. + return arg1 + arg2 + +func get_child_at(index: int) -> Node: + # 함수는 int를 사용하고 노드를 반환합니다. + return get_children()[index] +``` + +## 추가 자료 + +* [Godot 웹사이트](https://godotengine.org/) +* [Godot 문서](https://docs.godotengine.org/en/stable/) +* [GDScript 시작하기](https://docs.godotengine.org/en/stable/getting_started/scripting/gdscript/index.html) +* [NodePath](https://docs.godotengine.org/en/stable/classes/class_nodepath.html) +* [신호](https://docs.godotengine.org/en/stable/getting_started/step_by_step/signals.html) +* [GDQuest](https://www.gdquest.com/) +* [GDScript.com](https://gdscript.com/) \ No newline at end of file diff --git a/ko/git.md b/ko/git.md new file mode 100644 index 0000000000..b71f5c25ca --- /dev/null +++ b/ko/git.md @@ -0,0 +1,570 @@ +--- +category: tool +name: Git +filename: LearnGit.txt +contributors: + - ["Jake Prather", "http://github.com/JakeHP"] + - ["Leo Rudberg" , "http://github.com/LOZORD"] + - ["Betsy Lorton" , "http://github.com/schbetsy"] + - ["Bruno Volcov", "http://github.com/volcov"] + - ["Andrew Taylor", "http://github.com/andrewjt71"] + - ["Jason Stathopulos", "http://github.com/SpiritBreaker226"] + - ["Milo Gilad", "http://github.com/Myl0g"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Git은 분산 버전 제어 및 소스 코드 관리 시스템입니다. + +프로젝트의 일련의 스냅샷을 통해 이를 수행하며, 해당 스냅샷과 함께 작동하여 소스 코드의 버전을 관리하고 관리하는 기능을 제공합니다. + +## 버전 관리 개념 + +### 버전 관리란 무엇입니까? + +버전 관리는 시간이 지남에 따라 파일의 변경 사항을 기록하는 시스템입니다. + +### 중앙 집중식 버전 관리 대 분산 버전 관리 + +* 중앙 집중식 버전 관리는 파일 동기화, 추적 및 백업에 중점을 둡니다. +* 분산 버전 관리는 변경 사항 공유에 중점을 둡니다. 모든 변경 사항에는 고유한 ID가 있습니다. +* 분산 시스템에는 정의된 구조가 없습니다. git을 사용하여 SVN 스타일의 중앙 집중식 시스템을 쉽게 가질 수 있습니다. + +[추가 정보](https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control) + +### Git을 사용하는 이유 + +* 오프라인으로 작업할 수 있습니다. +* 다른 사람과 협업하기 쉽습니다! +* 브랜칭이 쉽습니다! +* 브랜칭이 빠릅니다! +* 병합이 쉽습니다! +* Git은 빠릅니다. +* Git은 유연합니다. + +## Git 아키텍처 + +### 리포지토리 + +파일, 디렉토리, 기록 기록, 커밋 및 헤드의 집합입니다. 소스 코드 데이터 구조로 상상해보십시오. 각 소스 코드 "요소"는 다른 것들 중에서 수정 기록에 대한 액세스를 제공하는 속성을 가집니다. + +Git 리포지토리는 .git 디렉토리 및 작업 트리로 구성됩니다. + +### .git 디렉토리 (리포지토리 구성 요소) + +.git 디렉토리에는 모든 구성, 로그, 브랜치, HEAD 등이 포함됩니다. +[자세한 목록.](https://gitready.com/advanced/2009/03/23/whats-inside-your-git-directory.html) + +### 작업 트리 (리포지토리 구성 요소) + +이것은 기본적으로 리포지토리의 디렉토리 및 파일입니다. 종종 작업 디렉토리라고 합니다. + +### 인덱스 (.git 디렉토리 구성 요소) + +인덱스는 git의 스테이징 영역입니다. 기본적으로 작업 트리를 Git 리포지토리와 분리하는 계층입니다. 이를 통해 개발자는 Git 리포지토리로 전송되는 내용에 대해 더 많은 권한을 가질 수 있습니다. + +### 커밋 + +Git 커밋은 작업 트리에 대한 일련의 변경 또는 조작의 스냅샷입니다. 예를 들어, 5개의 파일을 추가하고 2개의 다른 파일을 제거한 경우 이러한 변경 사항은 커밋(또는 스냅샷)에 포함됩니다. 그런 다음 이 커밋을 다른 리포지토리로 푸시하거나 푸시하지 않을 수 있습니다! + +### 브랜치 + +브랜치는 본질적으로 마지막으로 만든 커밋에 대한 포인터입니다. 커밋을 계속하면 이 포인터는 자동으로 최신 커밋을 가리키도록 업데이트됩니다. + +### 태그 + +태그는 기록의 특정 지점에 대한 표시입니다. 일반적으로 사람들은 이 기능을 사용하여 릴리스 지점(v1.0 등)을 표시합니다. + +### HEAD 및 head (.git 디렉토리 구성 요소) + +HEAD는 현재 브랜치를 가리키는 포인터입니다. 리포지토리에는 *활성* HEAD가 하나만 있습니다. +head는 모든 커밋을 가리키는 포인터입니다. 리포지토리에는 여러 개의 head가 있을 수 있습니다. + +### Git의 단계 +* 수정됨 - 파일이 변경되었지만 파일이 아직 Git 데이터베이스에 커밋되지 않았습니다. +* 스테이징됨 - 수정된 파일을 다음 커밋 스냅샷에 포함하도록 표시합니다. +* 커밋됨 - 파일이 Git 데이터베이스에 커밋되었습니다. + +## 명령어 + +### init + +빈 Git 리포지토리를 만듭니다. Git 리포지토리의 설정, 저장된 정보 등은 ".git"이라는 디렉토리(폴더)에 저장됩니다. + +```bash +$ git init +``` + +### config + +설정을 구성합니다. 리포지토리, 시스템 자체 또는 전역 구성(전역 구성 파일은 `~/.gitconfig`)에 대한 설정입니다. + +```bash +# 일부 기본 구성 변수 설정 및 인쇄 (전역) +$ git config --global user.email "MyEmail@Zoho.com" +$ git config --global user.name "My Name" + +$ git config --global user.email +$ git config --global user.name +``` + +[git config에 대해 자세히 알아보기.](https://git-scm.com/docs/git-config) + +### help + +각 명령에 대한 매우 자세한 가이드에 빠르게 액세스하거나 일부 의미 체계를 빠르게 상기시켜줍니다. + +```bash +# 사용 가능한 명령을 빠르게 확인 +$ git help + +# 사용 가능한 모든 명령 확인 +$ git help -a + +# 명령별 도움말 - 사용자 설명서 +# git help +$ git help add +$ git help commit +$ git help init +# 또는 git --help +$ git add --help +$ git commit --help +$ git init --help +``` + +### ignore files + +git에서 의도적으로 파일 및 폴더를 추적하지 않도록 합니다. 일반적으로 리포지토리에서 공유될 개인 및 임시 파일을 위한 것입니다. + +```bash +$ echo "temp/" >> .gitignore +$ echo "private_key" >> .gitignore +``` + +### status + +인덱스 파일(기본적으로 작업 복사본/리포지토리)과 현재 HEAD 커밋 간의 차이점을 표시합니다. + +```bash +# 브랜치, 추적되지 않은 파일, 변경 사항 및 기타 차이점을 표시합니다. +$ git status + +# git status에 대한 다른 "팁"을 알아보려면 +$ git help status +``` + +### add + +스테이징 영역/인덱스에 파일을 추가합니다. 새 파일을 스테이징 영역/인덱스에 `git add`하지 않으면 커밋에 포함되지 않습니다! + +```bash +# 현재 작업 디렉토리에 파일 추가 +$ git add HelloWorld.java + +# 중첩된 디렉토리에 파일 추가 +$ git add /path/to/file/HelloWorld.c + +# 정규 표현식 지원! +$ git add ./*.java + +# 작업 디렉토리의 모든 것을 스테이징 영역에 추가할 수도 있습니다. +$ git add -A +``` + +이것은 파일을 스테이징 영역/인덱스에만 추가하며, 작업 디렉토리/리포지토리에 커밋하지는 않습니다. + +### branch + +브랜치를 관리합니다. 이 명령을 사용하여 브랜치를 보고, 편집하고, 만들고, 삭제할 수 있습니다. + +```bash +# 기존 브랜치 및 원격 나열 +$ git branch -a + +# 새 브랜치 만들기 +$ git branch myNewBranch + +# 브랜치 삭제 +$ git branch -d myBranch + +# 브랜치 이름 바꾸기 +# git branch -m +$ git branch -m myBranchName myNewBranchName + +# 브랜치 설명 편집 +$ git branch myBranchName --edit-description +``` + +### tag + +태그를 관리합니다. + +```bash +# 태그 나열 +$ git tag + +# 주석이 달린 태그 만들기 +# -m은 태그와 함께 저장되는 태그 지정 메시지를 지정합니다. +# 주석이 달린 태그에 대한 메시지를 지정하지 않으면 Git은 편집기를 실행하여 입력할 수 있도록 합니다. +$ git tag -a v2.0 -m 'my version 2.0' + +# 태그 정보 표시 +# 태그 지정자 정보, 커밋이 태그 지정된 날짜 및 커밋 정보를 표시하기 전에 주석 메시지를 표시합니다. +$ git show v2.0 + +# 원격에 단일 태그 푸시 +$ git push origin v2.0 + +# 원격에 많은 태그 푸시 +$ git push origin --tags +``` + +### checkout + +작업 트리의 모든 파일을 인덱스 또는 지정된 트리의 버전과 일치하도록 업데이트합니다. + +```bash +# 리포지토리 체크아웃 - 기본적으로 마스터 브랜치 +$ git checkout + +# 지정된 브랜치 체크아웃 +$ git checkout branchName + +# 새 브랜치를 만들고 전환 +# "git branch ; git checkout "과 동일 + +$ git checkout -b newBranch +``` + +### clone + +기존 리포지토리를 새 디렉토리에 복제하거나 복사합니다. 또한 복제된 리포지토리의 각 브랜치에 대한 원격 추적 브랜치를 추가하여 원격 브랜치에 푸시할 수 있습니다. + +```bash +# learnxinyminutes-docs 복제 +$ git clone https://github.com/adambard/learnxinyminutes-docs.git + +# 얕은 복제 - 최신 스냅샷만 가져오는 더 빠른 복제 +$ git clone --depth 1 https://github.com/adambard/learnxinyminutes-docs.git + +# 특정 브랜치만 복제 +$ git clone -b master-cn https://github.com/adambard/learnxinyminutes-docs.git --single-branch +``` + +### commit + +인덱스의 현재 내용을 새 "커밋"에 저장합니다. 이 커밋에는 사용자가 만든 변경 사항과 메시지가 포함됩니다. + +```bash +# 메시지와 함께 커밋 +$ git commit -m "Added multiplyNumbers() function to HelloWorld.c" + +# 메시지와 함께 서명된 커밋 (user.signingkey는 GPG 키로 설정되어 있어야 함, 예: git config --global user.signingkey 5173AAD5) +$ git commit -S -m "signed commit message" + +# 새 파일을 제외하고 수정되거나 삭제된 파일을 자동으로 스테이징한 다음 커밋 +$ git commit -a -m "Modified foo.php and removed bar.php" + +# 마지막 커밋 변경 (이전 커밋을 새 커밋으로 삭제) +$ git commit --amend -m "Correct message" +``` + +### diff + +작업 디렉토리, 인덱스 및 커밋의 파일 간의 차이점을 표시합니다. + +```bash +# 작업 디렉토리와 인덱스 간의 차이점 표시 +$ git diff + +# 인덱스와 가장 최근 커밋 간의 차이점 표시. +$ git diff --cached + +# 작업 디렉토리와 가장 최근 커밋 간의 차이점 표시 +$ git diff HEAD +``` + +### grep + +리포지토리를 빠르게 검색할 수 있습니다. + +선택적 구성: + +```bash +# Travis Jeffery에게 감사드립니다. +# grep 검색 결과에 줄 번호 표시 설정 +$ git config --global grep.lineNumber true + +# 그룹화를 포함하여 검색 결과를 더 읽기 쉽게 만듭니다. +$ git config --global alias.g "grep --break --heading --line-number" +``` + +```bash +# 모든 java 파일에서 "variableName" 검색 +$ git grep 'variableName' -- '*.java' + +# "arrayListName"을 포함하고 "add" 또는 "remove"를 포함하는 줄 검색 +$ git grep -e 'arrayListName' --and \( -e add -e remove \ +``` + +Google은 당신의 친구입니다. 더 많은 예제를 보려면 +[Git Grep Ninja](https://travisjeffery.com/b/2012/02/search-a-git-repo-like-a-ninja) + +### log + +리포지토리에 대한 커밋을 표시합니다. + +```bash +# 모든 커밋 표시 +$ git log + +# 커밋 메시지 및 참조만 표시 +$ git log --oneline + +# 병합 커밋만 표시 +$ git log --merges + +# ASCII 그래프로 표시된 모든 커밋 표시 +$ git log --graph +``` + +### merge + +외부 커밋의 변경 사항을 현재 브랜치로 "병합"합니다. + +```bash +# 지정된 브랜치를 현재 브랜치로 병합합니다. +$ git merge branchName + +# 병합 시 항상 병합 커밋 생성 +$ git merge --no-ff branchName +``` + +### mv + +파일 이름 바꾸기 또는 이동 + +```bash +# 파일 이름 바꾸기 +$ git mv HelloWorld.c HelloNewWorld.c + +# 파일 이동 +$ git mv HelloWorld.c ./new/path/HelloWorld.c + +# 강제 이름 바꾸기 또는 이동 +# "existingFile"이 디렉토리에 이미 존재하며 덮어쓰게 됩니다. +$ git mv -f myFile existingFile +``` + +### pull + +리포지토리에서 가져와 다른 브랜치와 병합합니다. + +```bash +# 원격 "origin" 및 "master" 브랜치에서 새 변경 사항을 병합하여 로컬 리포지토리를 업데이트합니다. +# git pull +$ git pull origin master + +# 기본적으로 git pull은 원격 추적 브랜치에서 새 변경 사항을 병합하여 현재 브랜치를 업데이트합니다. +$ git pull + +# 원격 브랜치에서 변경 사항을 병합하고 로컬 리포지토리에 브랜치 커밋을 리베이스합니다. 예: "git fetch , git rebase /" +$ git pull origin master --rebase +``` + +### push + +브랜치의 변경 사항을 원격 및 브랜치로 푸시하고 병합합니다. + +```bash +# 로컬 리포지토리의 변경 사항을 "origin" 및 "master" 브랜치라는 원격으로 푸시하고 병합합니다. +# git push +$ git push origin master + +# 기본적으로 git push는 현재 브랜치의 변경 사항을 원격 추적 브랜치로 푸시하고 병합합니다. +$ git push + +# 현재 로컬 브랜치를 원격 브랜치와 연결하려면 -u 플래그를 추가하십시오: +$ git push -u origin master +# 이제 동일한 로컬 브랜치에서 푸시하고 싶을 때마다 바로 가기를 사용하십시오: +$ git push +``` + +### stash + +Stashing은 작업 디렉토리의 더러운 상태를 가져와 언제든지 다시 적용할 수 있는 미완성 변경 사항 스택에 저장합니다. + +git 리포지토리에서 일부 작업을 수행했지만 원격에서 가져오고 싶다고 가정해 보겠습니다. 일부 파일에 더러운(커밋되지 않은) 변경 사항이 있으므로 `git pull`을 실행할 수 없습니다. 대신 `git stash`를 실행하여 변경 사항을 스택에 저장할 수 있습니다! + +```bash +$ git stash +Saved working directory and index state \ + "WIP on master: 049d078 added the index file" + HEAD is now at 049d078 added the index file + (To restore them type "git stash apply") +``` + +이제 가져올 수 있습니다! + +```bash +git pull +``` + +`...변경 사항 적용...` + +이제 모든 것이 정상인지 확인하십시오. + +```bash +$ git status +# On branch master +nothing to commit, working directory clean +``` + +`git stash list`를 사용하여 지금까지 숨겨진 "덩어리"를 볼 수 있습니다. +"덩어리"는 후입선출 스택에 저장되므로 가장 최근 변경 사항이 맨 위에 있습니다. + +```bash +$ git stash list +stash@{0}: WIP on master: 049d078 added the index file +stash@{1}: WIP on master: c264051 Revert "added file_size" +stash@{2}: WIP on master: 21d80a5 added number to log +``` + +이제 스택에서 팝하여 더러운 변경 사항을 다시 적용해 보겠습니다. + +```bash +$ git stash pop +# On branch master +# Changes not staged for commit: +# (use "git add ..." to update what will be committed) +# +# modified: index.html +# modified: lib/simplegit.rb +# +``` + +`git stash apply`도 동일한 작업을 수행합니다. + +이제 다시 작업할 준비가 되었습니다! + +[추가 자료.](https://git-scm.com/book/en/v2/Git-Tools-Stashing-and-Cleaning) + +### rebase (주의) + +한 브랜치에 커밋된 모든 변경 사항을 가져와 다른 브랜치에 다시 적용합니다. +*공개 리포지토리에 푸시한 커밋은 리베이스하지 마십시오*. + +```bash +# experimentBranch를 마스터에 리베이스 +# git rebase +$ git rebase master experimentBranch +``` + +[추가 자료.](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) + +### reset (주의) + +현재 HEAD를 지정된 상태로 재설정합니다. 이를 통해 병합, 가져오기, 커밋, 추가 등을 취소할 수 있습니다. 훌륭한 명령이지만 무엇을 하는지 모르면 위험하기도 합니다. + +```bash +# 스테이징 영역을 재설정하여 최신 커밋과 일치하도록 합니다(디렉토리는 변경되지 않음). +$ git reset + +# 스테이징 영역을 재설정하여 최신 커밋과 일치하도록 하고 작업 디렉토리를 덮어씁니다. +$ git reset --hard + +# 현재 브랜치 팁을 지정된 커밋으로 이동합니다(디렉토리는 변경되지 않음). +# 모든 변경 사항은 여전히 디렉토리에 존재합니다. +$ git reset 31f2bb1 + +# 현재 브랜치 팁을 지정된 커밋으로 뒤로 이동하고 +# 작업 디렉토리를 일치시킵니다(커밋되지 않은 변경 사항 및 지정된 커밋 이후의 모든 커밋 삭제). +$ git reset --hard 31f2bb1 +``` + +### reflog (주의) + +Reflog는 지정된 기간(기본값 90일) 동안 수행한 대부분의 git 명령을 나열합니다. + +이를 통해 잘못된 git 명령을 되돌릴 수 있습니다(예: 리베이스로 인해 애플리케이션이 손상된 경우). + +이렇게 할 수 있습니다: + +1. `git reflog`를 사용하여 리베이스에 대한 모든 git 명령을 나열합니다. + +``` +38b323f HEAD@{0}: rebase -i (finish): returning to refs/heads/feature/add_git_reflog +38b323f HEAD@{1}: rebase -i (pick): Clarify inc/dec operators +4fff859 HEAD@{2}: rebase -i (pick): Update java.html.markdown +34ed963 HEAD@{3}: rebase -i (pick): [yaml/en] Add more resources (#1666) +ed8ddf2 HEAD@{4}: rebase -i (pick): pythonstatcomp spanish translation (#1748) +2e6c386 HEAD@{5}: rebase -i (start): checkout 02fb96d +``` + +2. 재설정할 위치를 선택합니다. 이 경우 `2e6c386` 또는 `HEAD@{5}`입니다. +3. 'git reset --hard HEAD@{5}'는 리포지토리를 해당 헤드로 재설정합니다. +4. 리베이스를 다시 시작하거나 그대로 둘 수 있습니다. + +[추가 자료.](https://git-scm.com/docs/git-reflog) + +### revert + +Revert는 커밋을 취소하는 데 사용할 수 있습니다. 프로젝트의 상태를 이전 지점으로 복원하는 재설정과 혼동해서는 안 됩니다. Revert는 지정된 커밋의 역인 새 커밋을 추가하여 되돌립니다. + +```bash +# 지정된 커밋 되돌리기 +$ git revert +``` + +### rm + +git add의 반대인 git rm은 현재 작업 트리에서 파일을 제거합니다. + +```bash +# HelloWorld.c 제거 +$ git rm HelloWorld.c + +# 중첩된 디렉토리에서 파일 제거 +$ git rm /pather/to/the/file/HelloWorld.c +``` + +### blame + +코드의 특정 부분을 검사하고 해당 줄을 마지막으로 수정한 작성자를 찾습니다. + +```bash +# 최신 수정된 줄의 작성자 찾기 +$ git blame google_python_style.vim +b88c6a1b (Google Python team 2019-12-30 13:45:23 -0800 12) " See the License for the specific language governing permissions and +b88c6a1b (Google Python team 2019-12-30 13:45:23 -0800 13) " limitations under the License. +b88c6a1b (Google Python team 2019-12-30 13:45:23 -0800 14) +222e6da8 (mshields@google.com 2010-11-29 20:32:06 +0000 15) " Indent Python in the Google way. +222e6da8 (mshields@google.com 2010-11-29 20:32:06 +0000 16) +222e6da8 (mshields@google.com 2010-11-29 20:32:06 +0000 17) setlocal indentexpr=GetGooglePythonIndent(v:lnum) +``` + +## 추가 정보 + +* [Git 브랜칭 배우기 - 웹에서 Git을 배우는 가장 시각적이고 대화형인 방법](https://learngitbranching.js.org/) + +* [Udemy Git 튜토리얼: 종합 가이드](https://blog.udemy.com/git-tutorial-a-comprehensive-guide/) + +* [Git 몰입 - git의 기본 사항을 안내하는 가이드 투어](https://gitimmersion.com/) + +* [git-scm - 비디오 튜토리얼](https://git-scm.com/videos) + +* [git-scm - 문서](https://git-scm.com/docs) + +* [Atlassian Git - 튜토리얼 및 워크플로](https://www.atlassian.com/git/) + +* [SalesForce 치트 시트](https://res.cloudinary.com/hy4kyit2a/image/upload/SF_git_cheatsheet.pdf) + +* [git - 간단한 가이드](https://rogerdudler.github.io/git-guide/index.html) + +* [Pro Git](https://git-scm.com/book/en/v2) + +* [초보자를 위한 Git 및 GitHub 소개 (튜토리얼)](https://product.hubspot.com/blog/git-and-github-tutorial-for-beginners) + +* [기본 명령 및 워크플로를 다루는 Git에 대한 새로운 보스턴 튜토리얼](https://www.youtube.com/playlist?list=PL6gx4Cwl9DGAKWClAD_iKpNC0bGHxGhcx) + +* [컴퓨터 과학자를 위한 Git](https://eagain.net/articles/git-for-computer-scientists/) \ No newline at end of file diff --git a/ko/gleam.md b/ko/gleam.md new file mode 100644 index 0000000000..ff6053920b --- /dev/null +++ b/ko/gleam.md @@ -0,0 +1,829 @@ +--- +name: Gleam +contributors: + - ["Antonio Ognio", "https://github.com/aognio/"] +filename: learngleam.gleam +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Gleam은 Erlang의 BEAM 가상 머신을 위한 새로운 언어로, 강력한 타입 시스템, 함수형 프로그래밍의 표현력, 그리고 OCaml, Rust, Elixir와 같은 언어에서 영감을 받은 친숙하고 현대적인 구문을 사용하여 고도로 동시적이고 내결함성이 있는 Erlang 런타임의 힘을 활용합니다. + +매우 현대적인 개발인 Gleam은 컴파일러, 빌드 도구, 코드 포맷터, 여러 편집기 통합 및 패키지 관리자와 함께 제공됩니다. + +더 큰 BEAM 생태계의 일부인 Gleam으로 만든 프로그램은 Erlang 또는 Elixir로 작성된 수천 개의 게시된 패키지를 사용할 수도 있습니다. + +언어의 디자인은 매우 간결하여 null 값, 예외, 명확한 오류 메시지 및 실용적인 타입 시스템이 없습니다. + +JavaScript는 추가로 컴파일 대상으로 지원되므로 브라우저 또는 다른 JS 지원 런타임에서 Gleam 코드를 실행할 수 있습니다. 이 기능을 사용하면 TypeScript 정의가 생성되므로 외부에서도 자신 있게 Gleam 코드와 상호 작용할 수 있습니다. + +```gleam +//// 이 네 개의 슬래시가 있는 주석은 모듈 수준입니다. +//// 이러한 종류의 주석은 전체 모듈을 설명하는 데 사용됩니다. + +import gleam/bool +import gleam/io +import gleam/int +import gleam/float +import gleam/list +import gleam/iterator +import gleam/option.{type Option, None, Some} +import gleam/result +import gleam/string +import gleam/string as text + +// 타입의 이름은 항상 대문자로 시작하며, 변수와 함수는 소문자로 시작하는 것과 대조됩니다. + +// pub 키워드를 사용하면 타입 별칭이 공개되어 다른 모듈에서 참조할 수 있습니다. + +pub type UserId = + Int + +pub fn main() { + io.println("Hello from learnxinmyminutes.com!") + // io.println("This statement got commented out by a two slashes comment.!") + + // 모듈은 모든 Gleam 코드가 구성되는 단위입니다. + // 모듈에는 함께 속하는 것처럼 보이는 타입, 함수 등의 정의가 많이 있습니다. + // 예를 들어, gleam/io 모듈에는 println과 같은 다양한 인쇄 함수가 포함되어 있습니다. + + // 모든 gleam 코드는 어떤 모듈에 있으며, 그 이름은 해당 파일의 이름에서 따옵니다. + // 예를 들어, gleam/io는 gleam이라는 디렉토리의 io.gleam이라는 파일에 있습니다. + + // Gleam에는 코드를 작성하고 편집할 때 도움이 되는 강력한 정적 타입 시스템이 있어 실수를 포착하고 변경할 위치를 보여줍니다. + // io.println(10) + // 이전 줄의 주석을 해제하면 io.println 함수는 정수가 아닌 문자열에서만 작동하므로 컴파일 타임 오류가 발생합니다. + + // 컴파일러는 다음과 같은 오류를 출력합니다: + // error: Type mismatch + // ┌─ /home/contributor/learnxinmyminutes/src/learnxinmyminutes.gleam:21:14 + // │ + // 21 │ io.println(10) + // │ ^^ + // + // Expected type: + // + // String + // + // Found type: + // + // Int + + // 숫자 작업 + + // Erlang 가상 머신에서 실행할 때 정수는 최대 및 최소 크기가 없습니다. + // JavaScript 런타임에서 실행할 때 정수는 JavaScript의 64비트 부동 소수점 숫자를 사용하여 표시됩니다. + + // 정수 산술 + io.debug(1 + 1) + io.debug(5 - 1) + io.debug(5 / 2) + io.debug(3 * 3) + io.debug(5 % 2) + + // 정수 비교 + io.debug(2 > 1) + io.debug(2 < 1) + io.debug(2 >= 1) + io.debug(2 <= 1) + + // 같음은 모든 유형에 대해 작동하며 구조적으로 확인됩니다. 즉, 두 값이 동일한 메모리 위치에 있는지 여부가 아니라 동일한 구조를 갖는 경우 동일합니다. + io.debug(1 == 1) + // True + io.debug(2 != 2) + // False + + // 표준 라이브러리 정수 함수 + io.debug(int.min(142, 137)) + // 137 + io.debug(int.clamp(-80, min: 0, max: 100)) + // 0 + io.debug(int.base_parse("10", 2)) + // Ok(2) + + // 2진수, 8진수 및 16진수 정수 리터럴 + io.debug(0b00001111) + io.debug(0o17) + io.debug(0xF) + + // 정수 가독성을 높이기 위해 밑줄 사용 + io.debug(1_000_000) + + // Gleam의 숫자 연산자는 오버로드되지 않으므로 부동 소수점 작업을 위한 전용 연산자가 있습니다. + + // 부동 소수점 산술 + io.debug(1.0 +. 1.5) + io.debug(5.0 -. 1.5) + io.debug(5.0 /. 2.5) + io.debug(3.0 *. 3.5) + + // 부동 소수점 비교 + io.debug(2.2 >. 1.3) + io.debug(2.2 <. 1.3) + io.debug(2.2 >=. 1.3) + io.debug(2.2 <=. 1.3) + + // 부동 소수점은 Erlang 및 JavaScript 런타임 모두에서 64비트 부동 소수점 숫자로 표시됩니다. + // 부동 소수점 동작은 해당 런타임에 따라 다르므로 두 런타임에서 약간 다르게 작동합니다. + + // JavaScript 런타임에서 부동 소수점 값에 대해 표현 가능한 최대(또는 최소) 값을 초과하면 Infinity(또는 -Infinity)가 됩니다. 두 무한대를 나누려고 하면 결과로 NaN이 됩니다. + + // BEAM에서 실행할 때 오버플로가 발생하면 오류가 발생합니다. 따라서 Erlang 런타임에는 NaN 또는 Infinity 부동 소수점 값이 없습니다. + + // 0으로 나누는 것은 오류가 아닙니다. + io.debug(3.14 /. 0.0) + // 0.0 + + // 표준 라이브러리 부동 소수점 함수 + io.debug(float.max(2.0, 9.5)) + // 9.5 + io.debug(float.ceiling(5.4)) + // 6.0 + + // 부동 소수점에 대한 밑줄도 지원됩니다. + io.debug(10_000.01) + + // 0으로 나누면 오버플로가 발생하지 않고 대신 0으로 정의됩니다. + + // 문자열 작업 + io.debug("⭐ Gleam ⭐ - 별") + io.debug( + "this + is + a + multi + line + string", + ) + io.debug("\u{1F600}") + // 웃는 얼굴 😀 출력 + + // 큰따옴표는 이스케이프할 수 있습니다. + io.println("\"X\" marks the spot") + + // 문자열 연결 + io.debug("One " <> "Two") + + // 문자열 함수 + io.debug(text.reverse("1 2 3 4 5")) + io.debug(text.append("abc", "def")) + + io.println(text.reverse("!desrever tog gnirts sihT")) + // "This string got reversed!" 출력 + + // 여러 이스케이프 시퀀스가 지원됩니다: + + // \" - 큰따옴표 + // \\ - 백슬래시 + // \f - 폼 피드 + // \n - 줄 바꿈 + // \r - 캐리지 리턴 + // \t - 탭 + + // 부울 연산자 + // || 및 && 연산자는 단락 평가로 작동합니다. + + io.debug(True && False) + // False + + io.debug(True && True) + // True + + io.debug(False || False) + // False + + io.debug(False || True) + // True + + // 부울 함수 + io.debug(bool.to_string(True)) + // "True" + + io.debug(bool.to_int(False)) + // 0 + + // 할당 + let x = "Original value" + io.debug(x) + + // `y`를 `x`의 값에 할당 + let y = x + io.debug(y) + + // `x`를 새 값에 할당 + let x = "New value" + io.debug(x) + + // `y`는 여전히 원래 값을 참조합니다. + io.debug(y) + + // Gleam에서 변수 및 함수 이름은 snake_case로 작성됩니다. + let answer_to_the_universe = 42 + io.debug(answer_to_the_universe) + + let and_everything = answer_to_the_universe + // 이제 변수를 사용하면 경고가 발생합니다. + + // warning: Unused variable + // ┌─ /home/contributor/learnxinmyminutes/src/learnxinmyminutes.gleam:199:7 + // │ + // 199 │ let and_everything = answer_to_the_universe + // │ ^^^^^^^^^^^^^^ This variable is never used + // Hint: You can ignore it with an underscore: `_and_everything`. + + // 타입 주석 + + let _name: String = "Gleam" + + let _is_cool: Bool = True + + let _version: Int = 1 + // 문서화 목적으로 유용하지만 컴파일러가 코드를 타입 검사하는 방식은 변경하지 않습니다. 주석이 타입과 일치하는지 확인하는 것 외에는 오류가 발생합니다. + + // let _has_wrong_type_annotation: Int = True + + // error: Type mismatch + // ┌─ /home/contributor/learnxinmyminutes/src/learnxinmyminutes.gleam:219:41 + // │ + // 219 │ let _has_wrong_type_annotation: Int = True + // │ ^^^^ + // + // Expected type: + // + // Int + // + // Found type: + // + // Bool + + // 타입 별칭 + let one: UserId = 1 + // 파일 시작 부분에서 UserId 타입 정의 참조 + + let two: Int = 2 + + // 별칭은 더 읽기 쉬운 코드와 더 정확한 문서를 만들기 위한 것입니다. + // 내부적으로는 여전히 동일한 유형의 값이므로 연산이 여전히 작동합니다. + io.debug(one + two) + // 3 + + // 블록: 범위 및 값 + let radius = { + let value = 100.0 + value + } + // io.debug(value) // <- "value"가 범위를 벗어났기 때문에 컴파일되지 않습니다. + + let area = 3.14159 *. radius *. radius + io.debug(area) + + // 괄호 대신 블록을 사용하여 연산을 그룹화합니다. + let n1 = { 3 + 2 } * 5 + let n2 = 3 + { 2 * 5 } + io.debug(n1 != n2) + // True + + // 목록 + + // Scrooge McDuck의 조카 + let nephews = ["Huey", "Dewey", "Louie"] + io.debug(nephews) + // ["Huey", "Dewey", "Louie"] + + // 불변하게 앞에 추가하여 원래 목록이 변경되지 않도록 합니다. + io.debug(["Donald", ..nephews]) + // ["Donald", "Huey", "Dewey", "Louie"] + + // 목록에 대한 일부 표준 라이브러리 함수 + + list.each(nephews, io.println) + // Huey + // Dewey + // Louie + + io.debug(list.drop(nephews, 2)) + // ["Louie"] + + more_examples() + more_function_examples() + generic_typing_examples() + beloved_pipelines_demo() + labels_in_function_calls() + showcase_flow_control() + more_on_recursion() + more_on_pattern_matching() + showcase_types() + more_on_types() + more_on_callbacks() + showcase_externals() + showcase_panic() +} + +// fn 키워드는 새 함수를 정의하는 데 사용됩니다. +fn multiply(a: Int, b: Int) -> Int { + // 명시적인 반환 없음 + // 마지막 표현식이 반환됩니다. + a * b +} + +// double 및 multiply 함수는 pub 키워드 없이 정의됩니다. +// 이것은 비공개 함수로 만들며, 이 모듈 내에서만 사용할 수 있습니다. +// 다른 모듈에서 사용하려고 하면 컴파일러 오류가 발생합니다. +fn double(a: Int) -> Int { + multiply(a, 2) +} + +// 공개 함수만 내보내지고 모듈 외부에서 호출할 수 있습니다. + +// 함수 인수 및 반환 값에 대한 타입 주석은 선택 사항이지만 명확성을 위해 좋은 관행으로 간주되며 의도적이고 사려 깊은 설계를 장려합니다. + +pub fn is_leap_year(year: Int) -> Bool { + { year % 4 == 0 } && { { year % 100 != 0 } || { year % 400 == 0 } } +} + +fn more_examples() { + // Debug는 또한 값을 반환하므로 해당 출력은 이 함수의 반환 값입니다. + io.debug(double(10)) + // 20 + io.debug(is_leap_year(2000)) + // True +} + +// Gleam은 고차 함수를 지원합니다: +// 변수에 할당하거나, 다른 함수에 인수로 전달하거나, 블록이나 다른 함수에서 값으로 반환할 수도 있습니다. +fn call_func_on_int(func: fn(Int) -> Int, value: Int) -> Int { + func(value) +} + +fn more_function_examples() -> Int { + io.debug(call_func_on_int(double, 2)) + // 4 + + let square = fn(x: Int) -> Int { x * x } + io.debug(square(3)) + // 9 + + // 정의 직후 익명 함수 호출 + io.debug(fn(x: Int) { x + 1 }(1)) + + // 클로저 예제 + let make_adder = fn(n: Int) -> fn(Int) -> Int { + fn(argument: Int) -> Int { argument + n } + } + + let adder_of_fives = make_adder(5) + io.debug(adder_of_fives(10)) + // 15 + + // 익명 함수는 명명된 함수와 상호 교환하여 사용할 수 있습니다. + io.debug(call_func_on_int(fn(x: Int) -> Int { x + 100 }, 900)) + // 1000 + + // 함수 데코레이터를 만들어 보겠습니다. + let twice = fn(wrapped_func: fn(Int) -> Int) -> fn(Int) -> Int { + fn(argument: Int) -> Int { wrapped_func(wrapped_func(argument)) } + } + let quadruple = twice(double) + io.debug(quadruple(1)) + + let quadruple_2 = fn(a: Int) -> Int { multiply(4, a) } + io.debug(quadruple_2(2)) + // 8 + + // 함수 캡처는 한 인수를 사용하고 즉시 다른 함수를 해당 인수로 호출하는 익명 함수를 만드는 약식 구문입니다. + let quadruple_3 = multiply(4, _) + io.debug(quadruple_3(4)) + // 16 +} + +// 제네릭 함수는 타입 변수를 사용하여 지원됩니다. +fn generic_twice(func: fn(value) -> value, argument: value) -> value { + func(func(argument)) +} + +// generic_twice에서 value는 타입 변수였습니다. +// generic_twice_decorator에서 the_type은 타입 변수입니다. +// 다른 변수와 마찬가지로 이름을 선택할 수 있습니다. +fn generic_twice_decorator( + func: fn(the_type) -> the_type, +) -> fn(the_type) -> the_type { + fn(argument: the_type) -> the_type { func(func(argument)) } +} + +fn generic_typing_examples() { + let double_integers = fn(a: Int) -> Int { a * 2 } + let double_floats = fn(a: Float) -> Float { a *. 2.0 } + io.debug(generic_twice(double_integers, 3)) + io.debug(generic_twice(double_floats, 3.0)) + + let quadruple_integers = generic_twice_decorator(double_integers) + let quadruple_floats = generic_twice_decorator(double_floats) + io.debug(quadruple_integers(1)) + // 4 + io.debug(quadruple_floats(1.0)) + // 4.0 +} + +// Gleam의 파이프 연산자 |>는 왼쪽 표현식의 결과를 가져와 오른쪽 함수의 인수로 전달합니다. +fn beloved_pipelines_demo() { + // 솔직히 말해서, 이 멋진 연산자 때문에 Gleam을 사용하고 싶으시죠? + ["hello", "world"] + |> list.intersperse(" ") + |> list.append(["!"]) + |> string.concat + |> string.capitalise + |> io.debug + + // 이것보다 더 깔끔하게 일치시키세요, 그렇죠? + io.debug( + string.capitalise( + string.concat( + list.append(list.intersperse(["hello", "world"], " "), ["!"]), + ), + ), + ) + + // Project Euler의 첫 번째 문제에 대한 해결책: + // URL: https://projecteuler.net/problem=1 + // 설명: 1000 미만의 3과 5의 모든 배수의 합을 찾으십시오. + iterator.iterate(1, fn(n) { n + 1 }) + |> iterator.take(1000 - 1) + |> iterator.filter(fn(n) { { n % 3 == 0 } || { n % 5 == 0 } }) + |> iterator.fold(from: 0, with: fn(acc, element) { element + acc }) + |> int.to_string + |> fn(sum_as_text: String) { + "Solution to Project Euler's problem #1: " <> sum_as_text + } + |> io.debug + // Project Euler의 문제 #1에 대한 해결책: 233168 +} + +// 각 인수 앞에 레이블을 추가할 수 있습니다. +fn call_func_on_int_with_labels( + func passed_func: fn(Int) -> Int, + value n: Int, +) -> Int { + passed_func(n) +} + +// 레이블과 인수는 동일한 이름을 가질 수 있습니다. +fn add_one(number number: Int) -> Int { + number + 1 +} + +fn add_two_integers(first n: Int, second m: Int) -> Int { + n + m +} + +fn labels_in_function_calls() -> Int { + // 인수에 레이블을 지정하므로 원하는 경우 순서를 바꿀 수 있습니다. + io.debug(call_func_on_int_with_labels(value: 8, func: double)) + io.debug(add_one(number: 1)) + // 2 + io.debug(string.contains(does: "theme", contain: "the")) + // True + // 레이블이 없는 인수는 먼저 와야 합니다. + io.debug(add_two_integers(2, second: 2)) + // 4 +} + +fn showcase_flow_control() { + // 패턴 매칭을 사용하여 실행할 코드를 선택하려는 경우 case를 사용하십시오. + // Gleam은 완전성 검사를 수행하여 모든 가능한 값이 포함되었는지 확인합니다. + // 그렇지 않으면 컴파일 오류가 발생합니다. + let puppies = ["Bear", "Frisco", "Ranger"] + let count = list.length(of: puppies) + { + "We have " + <> int.to_string(count) + <> " " + <> // 밑줄은 다른 모든 값과 일치합니다. + case count { + 1 -> "puppy" + _ -> "puppies" + } + } + |> io.debug + + // Gleam은 case 표현식의 패턴이 변수를 할당할 수 있도록 합니다. + { + "Puppy count: " + <> case list.length(puppies) { + 0 -> "None." + 1 -> "Just one." + other -> "As many as " <> int.to_string(other) <> " puppies." + } + } + |> io.debug + + // BEAM 언어는 설계상 함수형이며 Gleam도 예외는 아니므로 if, for 또는 while 구문이 없습니다. + + // 조건문에 대한 패턴 매칭 사용 + let answer = 42 + case answer == 42 { + True -> { + io.debug("This is the answer to the universe.") + } + False -> { + io.debug("This is the answer to something else.") + } + } + + // 반복 대신 재귀 사용 + from_one_to_ten(1) +} + +// 재귀 함수 +fn from_one_to_ten(n: Int) { + io.debug(n) + case n { + 10 -> Nil + _ -> from_one_to_ten(n + 1) + } +} + +// 재귀적으로 함수를 호출할 때 과도한 스택 프레임을 만들어 메모리 고갈을 피하기 위해 Gleam은 "꼬리 호출 최적화"를 지원합니다. 즉, 함수 호출이 함수가 하는 마지막 일인 경우 컴파일러가 현재 함수에 대한 스택 프레임을 재사용할 수 있습니다. + +pub fn fib(x: Int) -> Int { + // 공개 함수는 비공개 꼬리 재귀 함수를 호출합니다. + fib_loop(x, 1) +} + +fn fib_loop(x: Int, accumulator: Int) -> Int { + case x { + 1 -> accumulator + + // 이 함수가 하는 마지막 일은 자신을 호출하는 것입니다. + // 이전 수업에서 마지막으로 한 일은 두 정수를 곱하는 것이었습니다. + _ -> fib_loop(x - 1, accumulator + x) + } +} + +// Gleam은 case 표현식 내에서 [x, ..y] 패턴으로 목록의 첫 번째 요소와 나머지 부분을 패턴 매칭하는 것을 지원합니다. +fn reverse_list(the_list: List(value)) -> List(value) { + case the_list { + [head, ..tail] -> list.concat([reverse_list(tail), [head]]) + [] -> [] + } +} + +fn more_on_recursion() { + io.debug(fib(10)) + // 55 + io.debug(reverse_list([1, 2, 3])) +} + +fn more_on_pattern_matching() { + // 문자열에서 패턴 매칭할 때 <> 연산자는 특정 접두사가 있는 문자열과 일치하고 나머지를 변수에 할당합니다. + io.debug(case "Hello, Lucy" { + "Hello, " <> name -> "Greetings for " <> name + _ -> "Potentially no greetings" + }) + + // 대체 패턴이 지원되므로 여러 값에 대해 동일한 절이 사용됩니다. + let month = 2 + let year = 2024 + let number_of_days = case month { + 2 -> + case is_leap_year(year) { + False -> 28 + True -> 29 + } + 4 | 6 | 9 | 11 -> 30 + 1 | 3 | 5 | 7 | 8 | 10 | 12 -> 31 + _ -> 0 + } + io.debug("Number of days: " <> int.to_string(number_of_days)) + // 29 + + // 패턴 매칭의 가드: + // if 키워드를 사용할 때 표현식은 패턴이 일치하려면 True로 평가되어야 합니다. + let list_starts_with = fn(the_list: List(value), the_value: value) -> Bool { + case the_list { + [head, ..] if head == the_value -> True + _ -> False + } + } + io.debug(list_starts_with([10, 20, 30], 10)) + // True +} + +pub type Gender { + Male + Female + Other +} + +// 레코드: +// - 변형 지원 +// - 각 변형은 필드가 있는 구조체와 유사합니다. +pub type Shape { + Rectangle(base: Float, height: Float) + Triangle(base: Float, height: Float) +} + +// 하나의 변형이 있는 레코드는 구조체와 유사합니다. +pub type Point { + Point(x: Float, y: Float) +} + +fn showcase_types() { + // 튜플: + // - 다른 유형의 요소를 함께 혼합할 수 있습니다. + // - 해당 유형은 암시적입니다. 예: #{1, "Hello"}는 #{Int, String} 유형입니다. + // - 해당 요소는 숫자 인덱스로 액세스할 수 있습니다. + let tuple_01 = #(1, "Ferris", "rustacean", True) + let tuple_02 = #(1, "Lucy", "starfish", True) + io.debug(tuple_01) + io.debug(tuple_01.0) + // 1 + io.debug(tuple_02.1) + // Lucy + let #(_, name, species, _) = tuple_01 + io.debug(name <> " the " <> species) + + // 변수 할당을 포함한 튜플과의 패턴 매칭 + case tuple_02 { + #(_, name, _, True) -> io.debug(name <> " is a mascot.") + #(_, name, _, False) -> io.debug(name <> " is not a mascot.") + } + + // 패턴 매칭과 함께 사용자 지정 유형 사용 + let gender = Other + io.debug(case gender { + Male -> "Boy" + Female -> "Girl" + _ -> "Undetermined" + }) + + // 레코드 사용 + let rectangle_1 = Rectangle(base: 10.0, height: 20.0) + io.debug(rectangle_1.height) + // 10.3 + + let point_1 = Point(x: 3.2, y: 4.3) + io.debug(point_1) + + // 레코드 업데이트 + let point_2 = Point(..point_1, y: 5.7) + io.debug(point_2) + + // Gleam에서 값은 null일 수 없습니다. + // Nil은 해당 유형의 유일한 값입니다. + let some_var = Nil + let result = io.println("Hello!") + io.debug(some_var == result) + // True +} + +pub type Mineral { + Gold + Silver + Copper +} + +// 포함된 유형을 매개변수로 사용하는 제네릭 사용자 지정 유형 +pub type Purity(inner_type) { + Pure(inner_type) + Impure(inner_type) +} + +pub type Beverage { + Water + Juice +} + +// gleam/option 및 gleam/result 모듈의 기존 사용자 지정 유형은 null 가능 값 작업 및 잠재적 오류 처리를 용이하게 합니다. +pub type Person { + Person(name: String, nickname: Option(String)) +} + +pub type DiceError { + DiceValueOutOfRange +} + +fn checked_dice_value(value: Int) -> Result(Int, DiceError) { + case value { + 1 | 2 | 3 | 4 | 5 | 6 -> Ok(value) + _ -> Error(DiceValueOutOfRange) + } +} + +fn double_dice_value(value: Int) -> Result(Int, DiceError) { + case value { + 1 | 2 | 3 -> Ok(value * 2) + _ -> Error(DiceValueOutOfRange) + } +} + +fn more_on_types() { + let mineral_sample_01: Purity(Mineral) = Pure(Gold) + let mineral_sample_02 = Impure(Silver) + io.debug(mineral_sample_01) + io.debug(mineral_sample_02) + + // 유리는 비어 있거나 비어 있지 않을 수 있습니다. + let glass_01: Option(Beverage) = Some(Water) + let glass_02 = None + io.debug(glass_01) + io.debug(glass_02) + + // 사람은 별명이 있거나 없을 수 있습니다. + let person_01 = Person(name: "John", nickname: Some("The Ripper")) + let person_02 = Person(name: "Martin", nickname: None) + io.debug(person_01) + io.debug(person_02) + + // Result 유형의 값을 반환하는 함수 작업 + let dice_01 = 5 + case checked_dice_value(dice_01) { + Ok(checked_value) -> + io.debug("The value of " <> int.to_string(checked_value) <> " is OK.") + Error(DiceValueOutOfRange) -> + io.debug("The value of the dice is out of range") + } + + // 결과 값이 여전히 주사위의 면에 있는 숫자인 경우 값을 두 배로 늘리려고 합니다. + // 그렇지 않으면 최대값을 넣습니다. + 2 + |> checked_dice_value + |> result.try(double_dice_value) + |> result.unwrap(or: 6) + |> io.debug +} + +pub fn throw_dice_as_result() { + Ok(int.random(6) + 1) +} + +pub fn sum_dice_values(a: Int, b: Int) { + Ok(a + b) +} + +// 일급 함수 및 패턴 매칭에 베팅하면 쉽게 많은 들여쓰기가 발생할 수 있습니다. +fn roll_two_dices_without_use() { + result.try(throw_dice_as_result(), fn(first_dice) { + result.try(throw_dice_as_result(), fn(second_dice) { + result.map(sum_dice_values(first_dice, second_dice), fn(sum) { sum }) + }) + }) +} + +// use 표현식은 여전히 콜백을 사용하는 코드를 작성할 수 있지만 과도한 들여쓰기를 정리합니다: +// - 고차 함수에 대한 호출은 <- 연산자의 오른쪽에 있습니다. +// - 콜백 함수에 대한 인수 이름은 <- 연산자의 왼쪽에 있습니다. +// - 묶는 {} 블록의 나머지 모든 코드는 콜백 함수의 본문이 됩니다. +fn roll_two_dices_with_use() { + use first_dice <- result.try(throw_dice_as_result()) + use second_dice <- result.try(throw_dice_as_result()) + use sum <- result.map(sum_dice_values(first_dice, second_dice)) + // 이것은 가장 안쪽 콜백 함수의 나머지 코드입니다. + sum +} + +fn more_on_callbacks() { + io.debug(roll_two_dices_without_use()) + io.debug(roll_two_dices_with_use()) +} + +pub type DateTime + +// 외부 함수는 반환 유형을 주석으로 달아야 합니다. +@external(erlang, "calendar", "local_time") +pub fn now() -> DateTime + +fn showcase_externals() { + io.debug(now()) + // #(#(2024, 4, 6), #(14, 4, 16)) +} + +fn showcase_panic() { + // panic 키워드를 사용하여 의도적으로 실행을 중단하여 프로그램을 즉시 충돌시킬 수 있습니다. + case 3 == 2 { + True -> panic as "The equality operator is broken!" + False -> "Equality operator works for integers" + } + // todo 키워드를 사용하는 함수를 호출하면 충돌이 발생합니다. + // homework() +} + +pub fn homework() { + todo +} + +## 추가 자료 + +* [Gleam 공식 웹사이트](https://gleam.run/) +* [언어 둘러보기](https://tour.gleam.run/) - 라이브 코드 편집기 포함 +* [공식 문서](https://gleam.run/documentation/) +* [Gleam의 멋진 목록](https://github.com/gleam-lang/awesome-gleam) +* [Gleam을 위한 Exercism 트랙](https://exercism.org/tracks/gleam) + +공식 문서에는 다음에 익숙한 사람들을 위한 치트 시트가 있습니다: + +* [Elixir](https://gleam.run/cheatsheets/gleam-for-elixir-users) +* [Elm](https://gleam.run/cheatsheets/gleam-for-elm-users) +* [Erlang](https://gleam.run/cheatsheets/gleam-for-erlang-users) +* [PHP](https://gleam.run/cheatsheets/gleam-for-php-users) +* [Python](https://gleam.run/cheatsheets/gleam-for-python-users) +* [Rust](https://gleam.run/cheatsheets/gleam-for-rust-users) \ No newline at end of file diff --git a/ko/golfscript.md b/ko/golfscript.md new file mode 100644 index 0000000000..22a6e43a06 --- /dev/null +++ b/ko/golfscript.md @@ -0,0 +1,489 @@ +--- +name: GolfScript +filename: golfscript.gs +contributors: + - ["Nicholas S Georgescu", "http://github.com/ngeorgescu"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +GolfScript는 2007년 Darren Smith가 개발한 난해한 언어입니다. Ruby로 작성된 인터프리터가 있는 스크립팅 언어입니다. 매우 적은 문자로 매우 밀도 높은 코드를 작성할 수 있습니다. 언어의 주요 목표는 이름에서 알 수 있듯이 가능한 한 적은 키 입력으로 문제를 해결하는 것입니다. GolfScript 웹사이트의 예제 페이지에는 단 77자로 작성된 전체 스도쿠 해결 프로그램도 있습니다. + +GolfScript에 정말 능숙해지면 일부 (다소 어려운) 코딩 문제를 해결하기 위한 기본 언어로 쉽게 사용할 수 있습니다. Ruby보다 빠르지는 않지만, 다른 언어에서 전체 코드 줄을 대체할 수 있는 단일 GolfScript 문자로 인해 작성 속도가 매우 빠를 수 있습니다. + +GolfScript는 스택 사용을 기반으로 합니다. 따라서 이 튜토리얼은 일부 독립 실행형 코드 및 개별 결과와 달리 실제 스택에 대한 일련의 스택 작업으로 읽힙니다. 스택은 빈 목록으로 시작하며, 모든 것은 스택에 추가되거나 일부 항목을 팝하고 변환한 다음 다시 스택에 넣습니다. + +GolfScript 실행을 시작하려면 [GitHub 리포지토리](https://github.com/darrenks/golfscript)에서 golfscript.rb 파일을 가져올 수 있습니다. `$PATH`에 복사하십시오(필요에 따라 .rb를 삭제하고 chmod). 대화형 인터프리터(아래 튜토리얼 미러링)에서 GolfScript를 실행할 수 있습니다. GolfScript에 익숙해지면 "stdin"에서 실행을 시작할 수 있습니다. `~`로 시작하는 스크립트를 보면 파일에 드롭하고 `golfscript file.gs`로 실행하도록 설계되었을 가능성이 높습니다. 런타임에 입력을 파이프하거나 입력할 수 있습니다. + +``` +> 정의되지 않은 모든 것은 기술적으로 아무것도 평가되지 않으므로 주석이기도 합니다. +# 하지만 예약된 키워드나 구두점을 사용하면 문제가 발생할 수 있으므로 명시적으로 주석 처리하는 것이 좋습니다. +[] +> ###################################################################### +# 데이터 유형 +######################################################################## +> 1 # 여기서 스택에 1을 추가합니다. 모든 개체 항목은 스택에 항목을 추가합니다. +[1] +> 'abc' # 여기서 문자열을 추가합니다. 작은따옴표와 큰따옴표의 유일한 차이점은 큰따옴표를 사용하면 \' 및 \n 이외의 더 많은 것을 이스케이프할 수 있다는 것입니다. +# 이 튜토리얼에서는 중요하지 않습니다. +[1 "abc"] +> {+} # 스택에 넣을 수 있는 세 번째 유형의 개체는 블록입니다. +[1 "abc" {+}] +> ] # 이것은 이전의 모든 것을 가져와 네 번째 유형의 개체인 배열에 넣습니다. +# (버그 악용 제외 [2-1?] 이 네 가지 유형만 있습니다.) +[[1 "abc" {+}]] +> ; # 이 배열에서 discard 함수를 실행하여 스택을 지웁시다. +# ]; 문자를 입력하면 항상 스택이 지워집니다. +[] +> 1"abc"{+}]; # 줄 바꿈은 공백입니다. 지금까지 한 모든 것을 한 줄에 넣을 수 있으며 모두 똑같이 작동합니다. +######################################################################## +# 연산자 및 수학 +######################################################################## +[] +> 1 1 # 스택에 두 개의 1을 추가합니다. 첫 번째를 .으로 복제할 수도 있습니다. +[1 1] +> + # 수학은 스택의 맨 위에서 연산을 실행하여 수행됩니다. 이것은 독립 실행형 문자일 수 있습니다. 이것을 읽는 방법은 스택에 1을 넣고 스택에 다른 1을 넣은 다음 스택에서 상위 두 요소를 가져와 합산하고 스택으로 반환하는 + 연산을 실행하는 것입니다. 이것은 일반적으로 후위 표기법이라고 합니다. 약간 거슬릴 수 있지만 이것이 생각하는 방식입니다. 개체로 스택에 추가하고 연산자로 스택의 맨 위를 수정합니다. +[2] +> 8 1- # 빼기도 동일하게 작동합니다. N.B. 스택에 여전히 2가 있습니다. +# 이전부터 +[2 7] +> 10 2* # 곱셈도 동일하게 작동합니다. 곱은 스택에 추가됩니다. +[2 7 20] +> 35 4/ # 모든 나눗셈은 정수 나눗셈입니다. +[2 7 20 8] +> 35 4% # 모듈로 연산 +[2 7 20 8 3] +> 2 3? # 거듭제곱 +[2 7 20 8 3 8] +> 8~ # 부호 있는 정수에 대한 비트 "not" 함수 +[2 7 20 8 3 8 -9] +> -1~ # 이것은 0을 산출하며, ? 연산자에 유용합니다. +[2 7 20 8 3 8 -9 0] +> 5 3| # 또는: 7을 산출합니다. [1 0 1] | [0 1 1] => [1 1 1] +[2 7 20 8 3 8 -9 0 7] +> 5 3^ # xor: 6을 산출합니다. 패리티가 [1 1 0]에서 다르기 때문입니다. +[2 7 20 8 3 8 -9 0 7 6] +> 5 3& # 및: 1을 산출합니다. 두 비트에서 모두 활성 상태인 유일한 비트이기 때문입니다: [0 0 1] +[2 7 20 8 3 8 -9 0 7 6 1] +> ]; ################################################################### +# 부울 +######################################################################## +[] +> 5 3 +[5 3] +> < #스택에 두 개의 숫자를 추가한 다음 lessthan 연산을 수행합니다. +# 부울은 0, [], {}, ''인 경우 False이고 다른 모든 경우 True입니다. +[0] +> 5 3> # 보다 큼 연산. +[0 1] +> 5 3= #단일 등호는 연산자입니다. 다시 말하지만, 등호가 실행되기 전에 스택은 [0 1 5 3]을 읽고 등호 연산자는 상위 2개 값을 확인하고 다음을 산출합니다: +[0 1 0] +> ! #not, 0이면 1을 반환하고 그렇지 않으면 0을 반환합니다. +[0 1 1] +> ) #마지막 숫자를 증가시킵니다. +[0 1 2] +> ( #마지막 숫자를 감소시킵니다. +[0 1 1] +> ]; ################################################################### +# 스택 제어 +######################################################################## +[] +> 1 # 스택에 숫자를 넣습니다. +[1] +> . # 숫자를 복제합니다. +[1 1] +> ) # 증가 +[1 2] +> \ # 상위 두 항목을 뒤집습니다. +[2 1] +> 1$ # $는 스택의 n번째 항목을 이전 인덱스에 복사합니다. +# 여기서 1-인덱스 항목을 가져옵니다. +[2 1 2] +> 0$ # 0-인덱스 항목을 복사하려면 적절한 인덱스를 사용합니다. +# 이것은 . 연산과 동일합니다. +[2 1 2 2] +> ) # 증가 +[2 1 2 3] +> @ # 세 번째 항목을 맨 위로 가져옵니다. +[2 2 3 1] +> [@] # 이 트릭을 사용하여 상위 3개 항목을 뒤집고 배열에 넣습니다. +# 연산을 대괄호로 묶으면 결과가 배열로 뒤집힙니다. +# [+] 및 [-]와 같은 수학 연산도 마찬가지입니다. +[2 [3 1 2]] +> ]; # 또한 최대 두 번의 스트로크로 상위 세 항목을 모든 순열로 정렬할 수 있습니다. 아래는 3,~에 대한 결과입니다. + # => 0 1 2 (즉, 아무것도 하지 않음) + # \ => 0 2 1 + # @\ => 1 0 2 + # @ => 1 2 0 + # @@ => 2 0 1 + # \@ => 2 1 0 +[] +> ###################################################################### +# 배열 사용 +######################################################################## +[] +> 2, # 쉼표는 range() 함수입니다. +[[0 1]] +> , # 그리고 length() 함수이기도 합니다. +[2] +> ;4, # 네 개의 항목이 있는 배열을 함께 가져옵시다. +[[0 1 2 3]] +> ) # 마지막 값을 팝할 수 있습니다. +[[0 1 2] 3] +> + # 그리고 다시 넣습니다. +[[0 1 2 3]] +> ( # 첫 번째 값을 팝할 수 있습니다. +[[1 2 3] 0] +> \+ # 그리고 다시 넣습니다. +[[0 1 2 3]] +> 2- # 특정 값을 뺄 수 있습니다. +[[0 1 3]] +> [1 3] # 또는 값 목록 +[[0 1 3] [1 3]] +> - +[[0]] +> ! # 부울 연산은 목록, 문자열 및 블록에서도 작동합니다. 비어 있으면 1이고 그렇지 않으면 0입니다. 여기서 목록에는 0이 있지만 길이가 0이 아니므로 배열 전체는 여전히 True입니다... 따라서 "not"은 False입니다. +[0] +> ;4,(+ # 범위를 만들고 첫 번째 값을 팝하고 끝에 붙입니다. +[[1 2 3 0]] +> $ # 배열을 정렬하여 순서를 복원할 수도 있습니다. +[[0 1 2 3]] +> 1 > # < > 및 =을 사용하여 일치하는 인덱스를 가져올 수도 있습니다. 이것은 필터가 아닙니다! 이것은 인덱스 일치입니다. 1보다 큰 항목을 필터링하는 것은 {1>}로 수행됩니다. +[[1 2 3]] +> 2 < # 0-인덱스이므로 이 배열의 모든 것은 인덱스가 2보다 작습니다. 인덱스는 0과 1입니다. +[[1 2]] +> 1= # < 및 >는 한 항목이더라도 배열을 반환합니다. 등호는 항상 배열에서 드롭합니다. +[2] +> ;6,2% # 모듈로 연산자는 목록에서 단계로 작동합니다. +[[0 2 4]] +> ;4,2,-:a 3,2+:b # 부울도 목록에서 작동합니다. 두 개의 목록을 정의해 봅시다. +[[2 3] [0 1 2 2]] +> | # "또는" - 두 목록 중 하나에 나타나는 항목 집합을 반환합니다. 즉, "합집합" +[[2 3 0 1]] +> ;a b& # 1과 2 모두에 나타나는 항목 집합을 반환합니다. 예: "교집합" +[[2]] +> ;a b^ # 두 목록 간의 대칭 차집합을 반환합니다. +[[3 0 1]] +> ~ # 물결표는 목록에서 항목을 풉니다. +[3 0 1] +> ]; a +[2 3] +> 2? # 항목의 인덱스를 찾습니다. +[0] +> ;3a? +[1] +> 4a? # 항목이 없으면 -1을 반환합니다. 참고: 검색을 위해 요소와 배열의 순서는 중요하지 않습니다. [항목 목록?] 또는 [목록 항목?]일 수 있습니다. +[1 -1] +> ]; # 지우기 +[] +> 3,[4]* # 결합 또는 산재: 항목 사이에 항목을 넣습니다. +[[0 4 1 4 2]] +> ; 3,4* # 목록 곱셈 +[[0 1 2 0 1 2 0 1 2 0 1 2]] +> ;[1 2 3 2 3 5][2 3]/ # "분할" +[[[1] [] [5]]] +> ;[1 2 3 2 3 5][2 3]% # 모듈로는 "분할... 및 빈 항목 삭제"입니다. +[[[1] [5]]] +> ];#################################################################### +# 문자열 +######################################################################## +# 문자열은 배열처럼 작동합니다. +[] +> "use arch, am vegan, drive a stick" ', '/ # 분할 +[["use arch" "am vegan" "drive a stick"]] +> {'I '\+', BTW.'+}% # 맵 +[["I use arch, BTW." "I am vegan, BTW." "I drive a stick, BTW."]] +> n* # 결합. 변수 n은 기본적으로 줄 바꿈 문자로 정의됩니다. +["I use arch, BTW.\nI am vegan, BTW.\nI drive a stick, BTW."] +> n/ # 바꾸려면 분할하고 대체 문자열로 결합합니다. +[n "Also, not sure if I mentioned this, but" n]{+}* # 3개 항목 배열 합계 접기 +* # 그리고 결합을 사용하여 결과를 얻습니다. +n+ print # 그런 다음 결과를 예쁘게 팝/인쇄합니다. +I use arch, BTW. +Also, not sure if I mentioned this, but +I am vegan, BTW. +Also, not sure if I mentioned this, but +I drive a stick, BTW. +[] +> '22222'{+}* # 배열이 아닌 문자열을 접기 합산하면 ascii 값의 합계를 얻습니다. '2'는 50이므로 5배는 다음과 같습니다: +[250] +> ]; # 이것은 실제로 ascii 값을 배열에 넣는 영리한 트릭입니다. +[] +> "aabc" [{ ""+~}*] # 덧셈을 접고 문자열에 넣으면: +[[97 97 98 99]] +> {[.]""^}%""+ # ""^ 맵을 사용하여 문자열로 반환할 수 있습니다. +# 그리고 빈 문자열 결합. +["aabc"] +> {32-}% # 대부분의 매핑 연산은 예상대로 ascii 값에서 작동합니다. 예를 들어 A와 a의 차이가 32이므로 ascii 값에서 빼면 다음을 얻습니다: +["AABC"] +> ];################################################################### +# 블록 +######################################################################## +[] +> 3,~ # 압축되지 않은 배열로 시작 +[0 1 2] +> {+-} # 대괄호는 여러 함수를 포함할 수 있는 블록을 정의합니다. +[0 1 2 {+-}] +> ~ # 블록은 실행을 기다리는 함수입니다. 물결표는 이 경우 블록을 한 번 실행합니다. 상위 두 값인 1과 2를 더하고 0에서 뺐습니다. +[-3] +> ;10,~{+}5* # 곱셈은 블록을 여러 번 실행하는 데 작동합니다. +# 이 경우 마지막 6개 값을 5번 "더하기"를 실행하여 더했습니다. +[0 1 2 3 39] +> ];10,4> # 마지막 6개 항목을 가져와서 동일한 결과를 얻을 수 있습니다. +[[4 5 6 7 8 9]] +> {+}* # 그리고 덧셈에 대한 "접기" 함수를 사용합니다. +[39] +> # "접기"는 왼쪽에서 쌍으로 연산을 순차적으로 적용한 다음 결과를 덤프합니다. 복제 연산자를 사용하여 접을 때 어떤 일이 발생하는지 봅시다. 복제한 다음 복제된 항목을 부정하면 어떤 일이 발생하는지 명확합니다: +> ;4,{.-1*}* +[0 1 -1 2 -2 3 -3] +> ]{3%}, # 각 요소에 블록을 적용하여 목록을 필터링할 수 있습니다. +# 이 경우 3으로 나눈 나머지가 0이 아닌 숫자를 얻습니다. +[[1 -1 2 -2]] +> ;10,{3%0}, # 필터링을 위해 마지막 요소만 중요합니다. 여기서 0..9를 가져와 x mod 3을 계산한 다음 0을 반환합니다. 중간에 생성된 값은 순차적으로 덤프됩니다. +[0 1 2 0 1 2 0 1 2 0 []] +> ]; # 지우기 +[] +> 5,{5*}% # 맵은 배열의 각 연산을 수행하고 결과를 배열로 반환합니다. +[[0 5 10 15 20]] +> {.}% # 각 항목에 대해 중복을 매핑할 때 어떤 일이 발생하는지 봅시다. +[[0 0 5 5 10 10 15 15 20 20]] +> ];################################################################### +# 제어 흐름! +######################################################################## +# 이것은 스크립팅에서 가장 중요한 부분입니다. 대부분의 언어에는 for 루프와 while 루프라는 두 가지 주요 유형의 루프가 있습니다. 골프스크립트에는 많은 가능한 루프가 있지만 일반적으로 유용한 것은 몇 가지뿐입니다. for 루프는 목록에 대한 매핑, 필터링, 접기 및 정렬을 사용하여 구현됩니다. 예를 들어, 6의 계승을 다음과 같이 취할 수 있습니다: +6, # 0..5 가져오기 +{)}% # 목록 증가, 즉 "i++ for i in list"를 사용하여 1..6 가져오기 +{*}* # 곱셈으로 접기, 연산자 자체에 9자. +[720] +> 6),(;{*}* # 하지만 더 짧게 할 수 있을까요? 6을 증가시키고 0을 버리고 접어서 공간을 절약할 수 있습니다. 8자. +> # unfold를 사용하여 동일한 작업을 수행할 수도 있습니다. +1 6 # 누산기와 피승수, A와 M이라고 부르겠습니다. +{}{ # M인 동안 + . # M 복사, 이제 스택은 A M M입니다. + @ # A를 맨 위로 가져오기, 이제 M M A입니다. + * # 누산기에 M 적용, 이제 M A입니다. + \( # 순서 뒤집기, 이제 A M이고 M--입니다. +}/; # "끝", 피승수 목록 버리기 +# 이것은 사실상 while 루프 계승입니다. +[720 720] +> 1.{6>!}{.@*\)}/; # M++를 사용하여 M이 6보다 크지 않을 때까지 동일한 작업을 수행할 수도 있습니다. +> 1 6{.@*\(.}do; # 감소 접기와 동일하게 작동합니다. +[720 720 720] +> ]; #분명히 for 루프는 유한한 항목 집합에 대해 자연스럽게 실행되므로 계승에 이상적입니다. +######################################################################## +# 코드 작성 +######################################################################## +# 스크립트 작성 과정을 살펴보겠습니다. 몇 가지 트릭과 생각하는 방법이 있습니다. 간단한 예로 소수 체를 들어보겠습니다. 체질에는 몇 가지 전략이 있습니다. 첫째, 후보와 소수라는 두 개의 목록을 사용하는 전략이 있습니다. 후보에서 값을 팝하고, 그것으로 나눌 수 있는 모든 후보를 제거한 다음 소수에 추가합니다. 둘째, 숫자에 대한 필터링 작업만 있습니다. 0, 1 및 자신을 제외하고 0으로 나눈 나머지가 없는 숫자를 확인하는 프로그램을 작성하는 것이 더 짧을 것이라고 생각합니다. 느리지만 짧은 것이 왕입니다. 먼저 이 두 번째 전략을 시도해 보겠습니다. +[] +> 10 # 이 전략을 사용하여 목록을 필터링할 가능성이 높습니다. 목록의 한 요소로 작업하는 것이 가장 쉽습니다. 따라서 원하는 답을 알고 있는 몇 가지 예를 들어보겠습니다. +[10] +> .,2> # 복제하고 값 목록을 가져와 처음 두 개를 버립니다. +[10 [2 3 4 5 6 7 8 9]] +> {1$\%!}, # 10을 복제하고 요소 뒤로 이동한 다음 10 요소 %를 실행하고 답을 !하여 짝수 배수만 남깁니다. +[10 [2 5]] +> \; # 중간 결과가 솔루션에 나타나지 않도록 제거하고 싶습니다. +[[2 5]] +> 10.,2,-{1$\%!},\; # 좋습니다, 작은 함수를 한 줄에 모아보겠습니다. +[[2 5] [2 5]] +> ;; # 이제 이 전략을 사용하여 목록을 필터링합니다. !를 사용하여 결과를 부정해야 합니다. 따라서 인수가 있는 숫자를 얻으면 !가 0으로 평가되고 숫자가 필터링됩니다. +[] +> 10,{.,2,-{1$\%!},\;!}, # 처음 10개 숫자에 대해 필터링해 보겠습니다. +[[0 1 2 3 5 7]] +> 2> # 이제 0과 1을 버릴 수 있습니다. +[[2 3 5 7]] +> 4.?,{.,2,-{1$\%!},\;!},2> # 트릭: 몇 바이트로 큰 숫자를 생성하는 쉬운 방법은 복제하고 거듭제곱하는 것입니다. 4.?는 256이고 9.?는 387420489입니다. +[[2 3 5 7] [2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 +97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 +197 199 211 223 227 229 233 239 241 251]] +> ];'4.?,{.,2,-{1$\%!},\;!},2>', # p<256에 대한 코드 길이는 얼마입니까? +[25] +> ; # 이것은 25자입니다. 더 잘할 수 있을까요?! +[] +> []99,2> # 첫 번째 전략으로 가겠습니다. 빈 소수 목록과 후보 목록으로 시작하겠습니다. +[[] [2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 +29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 +55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 +81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98]] +> (\ # 왼쪽 팝하고 왼쪽 남기기, 필터로 이 값을 복사할 것입니다. +[[] 2 [3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 +29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 +55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 +81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98]] +> {1$\%%}, # 팝된 항목으로 0 mod인 모든 것을 필터링합니다. 스택에 하나 뒤로 +[[] 2 [3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 +53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97]] +> @@+ # 좋습니다, 2로 나눌 수 있는 모든 값이 목록에서 제거되었습니다! 이제 실행 중인 소수 목록에 추가해야 합니다. +[[3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 +57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97] [2]] +> \ # 다시 교환합니다. 이제 후보 목록이 비어 있으면 끝났다는 것이 분명합니다. do 루프로 시도해 보겠습니다. 팝 확인을 위해 최종 값을 복제해야 합니다. 따라서 점을 추가합니다. +[[2] [3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 +55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97]] +> {(\{1$\%%},@@+\}.}do; +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]] +> ; # 좋습니다, 작동했습니다. 초기화부터 시작해 보겠습니다. +[]4.?,2>{(\{1$\%%},@@+\}.}do; # 그리고 작업을 확인합니다. +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 +103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 +211 223 227 229 233 239 241 251]] +> ,'[]99,2>{(\{1$\%%},@@+\}.}do;', # 이것은 얼마나 깁니까? +[26] +> ]; # 와, 이 솔루션은 26자밖에 안 되고 훨씬 더 효과적입니다. 여기서 더 짧게 할 방법이 보이지 않습니다. unfold를 사용하면 더 잘할 수 있을까요? 여기서 전략은 unfold를 사용한 다음 각 테이블에서 첫 번째 값을 가져오는 것입니다. +[] +> 99,2> # 후보 목록으로 시작 +[[2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 +30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 +56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 +82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98]] +> (\ {1$\%%}, # 팝하고 필터링 +[2 [3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 +55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97]] +> (\ {1$\%%}, # 다시! +[2 3 [5 7 11 13 17 19 23 25 29 31 35 37 41 43 47 49 53 55 59 61 65 67 71 73 77 +79 83 85 89 91 95 97]] +89 91 95 97]] +> {}{(\{1$\%%},}/ # 좋습니다, 작동할 것 같습니다. unfold에 넣어보겠습니다. +[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 [[5 7 +11 13 17 19 23 25 29 31 35 37 41 43 47 49 53 55 59 61 65 67 71 73 77 79 83 85 +89 91 95 97] [7 11 13 17 19 23 29 31 37 41 43 47 49 53 59 61 67 71 73 77 79 83 +89 91 97] [11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97] [13 +17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97] [17 19 23 29 31 37 41 +43 47 53 59 61 67 71 73 79 83 89 97] [19 23 29 31 37 41 43 47 53 59 61 67 71 73 +79 83 89 97] [23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97] [29 31 37 41 +43 47 53 59 61 67 71 73 79 83 89 97] [31 37 41 43 47 53 59 61 67 71 73 79 83 89 +97] [37 41 43 47 53 59 61 67 71 73 79 83 89 97] [41 43 47 53 59 61 67 71 73 79 +83 89 97] [43 47 53 59 61 67 71 73 79 83 89 97] [47 53 59 61 67 71 73 79 83 89 +97] [53 59 61 67 71 73 79 83 89 97] [59 61 67 71 73 79 83 89 97] [61 67 71 73 +79 83 89 97] [67 71 73 79 83 89 97] [71 73 79 83 89 97] [73 79 83 89 97] [79 83 +89 97] [83 89 97] [89 97] [97]]] +> ]; # 각 단계에서 생성된 후보 목록을 버리고 각 단계에서 unfold가 남긴 항목(소수)을 목록에 넣습니다. +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]] +> ]; # 지우고 더 큰 숫자로 시도해 보겠습니다. +[] +> 4.?,2>{}{(\{1$\%%},}/;] +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 +103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 +211 223 227 229 233 239 241 251]] +>;'4.?,2>{}{(\{1$\%%},}/;]', # 솔루션의 길이를 찾습니다. +[21] +> ]; # 소수에 대해 21자밖에 안 됩니다. 이제 do 루프를 사용하여 항목을 남기는 이 전략을 실제로 사용할 수 있는지 봅시다. 훨씬 더 짧아집니다! +> 3.?,2> # 후보 +[[2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26]] +> (\ {1$\%%}, # 팝하고 필터링 +[2 [3 5 7 9 11 13 15 17 19 21 23 25]] +> (\ {1$\%%}, # 다시! +[2 3 [5 7 11 13 17 19 23 25]] +> {(\{1$\%%},.}do;] # do 루프에서 시도하고 do 루프 끝에 있는 빈 후보 목록을 버립니다. 닫는 중괄호 앞에 점을 잊지 마십시오! +[[2 3 5 7 11 13 17 19 23]] +> ;4.?,2>{(\{1$\%%},.}do;] # 작업을 확인합니다. +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 +103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 +211 223 227 229 233 239 241 251]] +> ;'4.?,2>{(\{1$\%%},.}do;]', +[21] +>]; # 여전히 21자입니다. 윌슨의 정리로 알려진 소수 테스트를 시도해 볼 수 있는 또 다른 것이 있습니다. 이 테스트를 사용하여 항목을 필터링해 볼 수 있습니다. +[] +> '4.?,2>{.,(;{*}*.*\%},'.~\, # 실행하고 길이를 가져옵니다. +[[2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 +103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 +211 223 227 229 233 239 241 251] 21] +> ; # 여전히 21자입니다! 이 숫자는 꽤 좋고 이길 방법이 명확하지 않다고 생각합니다. GolfScript의 문제는 항상 당신이 생각하지 못한 트릭을 생각하는 사람이 있다는 것입니다. 예를 들어, {(}{.2%{3*)}{2/}if}/의 Collatz 시퀀스 생성기로 잘하고 있다고 생각할 수 있습니다. {(}{3*).2%6\?/}/가 훨씬 짧고 깨끗하다는 것을 알게 될 때까지 - unfold 연산은 길이가 거의 절반입니다! +######################################################################## +# GolfScript 읽는 방법 +######################################################################## +# GolfScript 배너에서 gcd를 가져오겠습니다. 다음과 같이 시작합니다: +[] +> '2706 410'~ # 목록을 평가하고 결과를 스택에 덤프하는 것은 매우 간단합니다. stdin에서 읽는 것이 일반적이므로 ~로 압축을 풀어야 합니다. +[2706 410] +> . # do 루프가 무엇을 하는지 알고 싶습니다. 가장 좋은 방법은 중괄호를 버리고 루프를 한 번에 한 명령씩 실행하는 것입니다. 복제합니다. +[2706 410 410] +> @\ # 재정렬합니다. +[410 2706 410] +> % # 모듈로를 취합니다. +[410 246] +> .@\% # 반복합니다. 닫는 중괄호 앞에 있는 마지막 점을 실행할 필요가 없습니다. 이것은 루프 조건을 확인하기 위해 팝되는 값이므로. 세미콜론으로 팝하여 직접 루프 끝을 복제할 수도 있습니다. +[246 164] +> .@\% # 다시! +[164 82] +> .@\% # 그리고 마침내 0에 도달합니다. 루프가 종료되고 ;가 0을 팝하여 82의 gcd를 남깁니다. +[82 0] +> ]; 2706 410{1$1$%.}do # 이것은 유클리드 방법에 대해 알아야 합니다. 여기에서 숫자를 순서대로 보여주는 더 명확한 방법을 시도할 수도 있습니다. +[2706 410 246 164 82 0] +>]; # 따라서 때로는 수학을 아는 것이 도움이 되며 즉시 명확하지 않은 쉬운 트릭에 의존하는 짧은 알고리즘을 작성할 수 있습니다. +[] +> # 예제 페이지에 있는 스도쿠 해결 프로그램을 살펴보겠습니다. 압축 해제 단계는 건너뛰겠습니다. +[2 8 4 3 7 5 1 6 9 0 0 9 2 0 0 0 0 7 0 0 1 0 0 4 0 0 2 0 5 0 0 0 0 8 0 0 0 0 8 +0 0 0 9 0 0 0 0 6 0 0 0 0 4 0 9 0 0 1 0 0 5 0 0 8 0 0 0 0 7 6 0 4 4 2 5 6 8 9 7 +3 1]:a 0?:b # 다시 그리드는 배열에 넣습니다. 이제 다음 단계는 "@" 기호를 작업 그리드로 정의하는 것입니다. 이것은 "@9"가 두 개의 기호로 해석되는 반면, 변수로 "a"와 같은 것을 사용하면 "a9"가 단일 기호로 해석되고 이것이 정의되지 않았기 때문에 실행 시간에 실행되지 않기 때문입니다. 추가 문자인 공백이 필요합니다. 반면에 내장 기능을 재정의하는 것은 혼란스러우므로 "@" 및 "^" 정의에 "a"와 "b"를 사용하겠습니다. 따라서 그리드는 "a"이고 첫 번째 0의 0-인덱스 위치는 9인 "b"입니다. +[9] +> ~! # 이것은 find에 대한 값이 -1이 아님을 확인합니다. 즉, -1~은 0으로 평가되므로 !는 0이 아닌 값을 만듭니다. ?~!는 "목록에 없음"에 대한 훌륭한 트릭입니다. +[0] +> {@p}* # 이것은 이전 값만큼 그리드를 인쇄하며, 이것이 이 것이 "완료"되는 방식입니다. 따라서 그리드에 0이 없으면 인쇄됩니다. +> 10, # 0-9 숫자를 가져옵니다. 원래 값이 0이므로 행이나 열을 볼 때 0이 보장되므로 0이 제거됩니다. +[[0 1 2 3 4 5 6 7 8 9]] +> a 9/ # 원래 그리드를 행 단위로 분할합니다. +b 9/ # 확인된 값의 행을 가져옵니다. 이 경우 두 번째 행입니다. += # 그리고 해당 행을 가져와 +- # 후보에서 해당 숫자를 제거합니다. +[[1 3 4 5 6 8]] +> a # 스택에 그리드를 넣습니다. +b 9% # 0의 열을 가져옵니다. +> # 그리드의 첫 번째 x 값을 버립니다. +9% # 9번째 숫자마다 가져옵니다. 이제 0이 있는 열이 있습니다. +> - # 후보 목록에서 해당 항목을 가져옵니다. +[[1 3 5 6]] +> a 3/ # 그리드를 3개 길이의 배열로 분할합니다. +b 9% # 0의 열을 가져옵니다. +3/ # 열이 왼쪽(0), 중간(1) 또는 오른쪽(2) 삼중항에 있습니까? + > # 해당 3개 그룹을 가져옵니다. +3% # 3번째마다 가져옵니다. 이제 9개 그룹이 있습니다 - 그리드의 왼쪽입니다. +3/ # 해당 9개 그룹을 3분의 1로 나눕니다. +b 27/ # 0이 그리드의 상단(0), 중간(1) 또는 하단(2) 3분의 1에 있었습니까? += # 상단이므로 상단 삼중항 그룹을 가져옵니다. 이제 0이 있는 스도쿠 그리드의 1/9이 있습니다. +[[1 3 5 6] [[2 8 4] [0 0 9] [0 0 1]]] +> {+}*- # 해당 목록을 평탄화하고 후보에서 해당 항목을 제거합니다. +# 이제 문제의 위치에 대해 그리드의 현재 상태를 고려할 때 가능한 값이 있습니다! 이 목록이 비어 있으면 이전 값과 모순됩니다. +[[3 5 6]] +> 0= # {a b<\+a 1 b+>+}/ # 이제 이 unfold 연산을 수행했습니다. 실행하면 그리드가 다시 나타납니다. 어떻게 작동합니까?! "each" []{}/ 연산에서 첫 번째 값을 가져오겠습니다. 이것이 매핑 상황에서 어떤 일이 일어나고 있는지 파악하는 가장 좋은 방법입니다. +[3] +> a # 그리드를 가져옵니다. +b< # 0까지 그리드를 가져옵니다. +\+ # 그리고 3 값을 붙입니다. +[[2 8 4 3 7 5 1 6 9 3]] +> a 1b+>+ # 그리고 나머지 그리드를 추가합니다. 참고: 1b+는 b)보다 길지만 동일하므로 1자 더 잘할 수 있습니다. +[[2 8 4 3 7 5 1 6 9 3 0 9 2 0 0 0 0 7 0 0 1 0 0 4 0 0 2 0 5 0 0 0 0 8 0 0 0 0 8 +0 0 0 9 0 0 0 0 6 0 0 0 0 4 0 9 0 0 1 0 0 5 0 0 8 0 0 0 0 7 6 0 4 4 2 5 6 8 9 7 +3 1]] +> 1;; # 그리고 do 블록은 상관없이 다시 실행됩니다. 따라서 마지막 숫자를 해결하면 이 루프가 계속 실행되는 이유가 분명합니다! 일부 제어 흐름에 대해 몇 바이트를 추가할 수 있지만 작동하면 작동하고 짧은 것이 왕입니다. +[] + +# 다음 단계로 나아가기 위한 마무리 팁: +# 0. 값을 교환하는 것보다 lookback을 사용하는 것이 더 효과적일 수 있습니다. 예를 들어, 1$1$과 \.@.@.\는 동일한 작업을 수행합니다: 마지막 두 항목을 복제하지만 전자가 더 명확하고 짧습니다. +# 1. 골프스크립트는 정수 시퀀스를 가지고 놀거나 다른 멋진 수학을 하는 데 재미있을 수 있습니다. 따라서 인생을 더 쉽게 만들기 위해 자신만의 함수를 정의하는 것을 두려워하지 마십시오. +> { $0= }:min; { $-1= }:max; { .,(;{*}*.*\% }:isprime; { .| }:set; # 등 +# 2. 다른 언어로 의사 코드를 작성하거나 스크립트를 포팅하여 어떤 일이 일어나고 있는지 파악하십시오. 특히 이 전략을 대수 엔진과 결합할 때 유용합니다. 예를 들어, 예제 페이지 1000자리 파이를 파이썬으로 포팅하고 다음을 얻을 수 있습니다: +# import sympy as sp +# a, k = sp.var('a'), list(range(20))[1::2] +# for _ in range(len(k)-1): +# m = k.pop() +# l = k.pop() +# k.append(((l+1)//2*m)//(l+2)+2*a) +# print(str(k[0])) +# "2*a + floor(2*a/3 + floor(4*a/5 + 2*floor(6*a/7 + 3*floor(8*a/9 + 4*floor(10*a/11 + 5*floor(12*a/13 + 6*floor(14*a/15 + 7*floor(16* +# a/17 + 72/17)/15)/13)/11)/9)/7)/5)/3)"을 제공합니다... 10.3??:a;20,-2%{2+.2/@*\/a 2*+}*보다 훨씬 더 명확하게 어떤 일이 일어나고 있는지 알 수 있습니다. 특히 언어에 익숙하지 않은 경우 +# 3. 약간의 수학은 큰 도움이 됩니다. 위의 소수 테스트는 윌슨의 정리를 사용합니다. 인수를 테스트하는 비슷한 프로그램 {:i,(;{i\%!},(;!}:isprime은 더 길고 느립니다. 또한 위에서 논의했듯이 Collatz는 (3x+1)을 수행한 다음 6의 거듭제곱으로 나눌 수 있다는 것을 인식하면 훨씬 짧습니다. ((3x+1) mod 2). (x가 짝수이면 (3x+1)은 이제 홀수이므로 3x+1 div 6은 x/2입니다.) 조건부 및 중복을 피하려면 때로는 그러한 통찰력이 필요합니다. 그리고 물론, 이 파이의 연분수를 모르면 간결한 코드 블록으로 계산하기 어렵습니다. +# 4. 변수를 정의하고 배열을 사용하는 것을 두려워하지 마십시오! 특히 4개 이상의 항목을 섞어야 하는 경우. +# 5. 나중에 항목을 수집하거나 추가하거나 항목을 배열에 유지하는 데이터 구조를 강제로 사용하는 대신, 나중에 [some_long_script]를 사용하여 많은 항목을 배열에 묶는 것을 두려워하지 마십시오. +# 6. 때로는 공백을 추가하지 않고 대칭 집합 차이를 수행하기 위해 ^를 사용하여 해결할 수 있는 - 다음에 int가 오는 곤경에 처할 수 있습니다. +# 7. "#{require 'net/http';Net::HTTP.get_response(URI.parse(address)).body}"는 인터넷에서 모든 페이지 소스를 가져올 수 있으며, 'address'를 URL로 대체합니다. OEIS b-파일 또는 단어 목록 등으로 시도해 보십시오. 파일을 읽으려면 더 짧은 "#{File.open('filename.txt').read}"를 사용할 수도 있습니다. GolfScript는 "#{any_ruby_code_here}"를 실행하고 결과를 스택에 추가할 수 있습니다. +# 8. 무엇이든 무엇이든 설정할 수 있으며, 골프에 유용할 수 있습니다: +# 3:^;^2? => 9, 이것은 ^를 3으로 설정하고 3 2 ? => 9이기 때문입니다. +# 3:a;a2? => 경고: 빈 스택에서 팝 - a2가 없기 때문입니다. +# 3:a;a 2? => 9 - 다시 작동하지만 ^2보다 한 자 더 걸립니다. +# 일반적으로 코드에서 마지막 몇 자를 짜내려고 할 때만 이 작업을 수행하고 싶을 것입니다. 왜냐하면 환경을 망치기 때문입니다. + +* [온라인에서 GolfScript 실행](https://tio.run/#golfscript) +* [GolfScript 문서](http://www.golfscript.com/golfscript/builtin.html) +* [유용한 StackExchange 스레드](https://codegolf.stackexchange.com/questions/5264/tips-for-golfing-in-golfscript) +* [GitHub의 GolfScript](https://github.com/darrenks/golfscript) + +``` \ No newline at end of file diff --git a/ko/groovy.md b/ko/groovy.md new file mode 100644 index 0000000000..d1fb0bfb1e --- /dev/null +++ b/ko/groovy.md @@ -0,0 +1,430 @@ +--- +name: Groovy +contributors: + - ["Roberto Pérez Alcolea", "http://github.com/rpalcolea"] +filename: learngroovy.groovy +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +[Groovy](http://www.groovy-lang.org/)는 Java 플랫폼을 위한 동적 언어입니다. + +```groovy +/* + 설정: + + 1) SDKMAN 설치 - http://sdkman.io/ + 2) Groovy 설치: sdk install groovy + 3) groovyConsole을 입력하여 groovy 콘솔 시작 + +*/ + +// 한 줄 주석은 두 개의 슬래시로 시작합니다. +/* +여러 줄 주석은 이렇습니다. +*/ + +// Hello World +println "Hello world!" + +/* + 변수: + + 나중에 사용하기 위해 변수에 값을 할당할 수 있습니다. +*/ + +def x = 1 +println x + +x = new java.util.Date() +println x + +x = -3.1499392 +println x + +x = false +println x + +x = "Groovy!" +println x + +/* + 컬렉션 및 맵 +*/ + +//빈 목록 만들기 +def technologies = [] + +// 또는 데이터로 목록 만들기 +technologies = ["Kotlin", "Swift"] + +/*** 목록에 요소 추가 ***/ + +// Java와 마찬가지로 +technologies.add("Grails") + +// 왼쪽 시프트는 추가하고 목록을 반환합니다. +technologies << "Groovy" + +// 여러 요소 추가 +technologies.addAll(["Gradle","Griffon"]) + +/*** 목록에서 요소 제거 ***/ + +// Java와 마찬가지로 +technologies.remove("Griffon") + +// 빼기도 작동합니다. +technologies = technologies - 'Grails' + +/*** 목록 반복 ***/ + +// 목록의 요소를 반복합니다. +technologies.each { println "Technology: $it"} +technologies.eachWithIndex { it, i -> println "$i: $it"} + +/*** 목록 내용 확인 ***/ + +//목록에 요소가 포함되어 있는지 평가합니다(부울). +contained = technologies.contains( 'Groovy' ) + +// 또는 +contained = 'Groovy' in technologies + +// 여러 내용 확인 +technologies.containsAll(['Groovy','Grails']) + +/*** 목록 정렬 ***/ + +// 목록 정렬 (원본 목록 변경) +technologies.sort() + +// 원본을 변경하지 않고 정렬하려면 다음을 수행할 수 있습니다: +sortedTechnologies = technologies.sort( false ) + +/*** 목록 조작 ***/ + +//목록의 모든 요소 바꾸기 +Collections.replaceAll(technologies, 'Gradle', 'gradle') + +//목록 섞기 +Collections.shuffle(technologies, new Random()) + +//목록 지우기 +technologies.clear() + +//빈 맵 만들기 +def devMap = [:] + +//값 추가 +devMap = ['name':'Roberto', 'framework':'Grails', 'language':'Groovy'] +devMap.put('lastName','Perez') + +//맵의 요소를 반복합니다. +devMap.each { println "$it.key: $it.value" } +devMap.eachWithIndex { it, i -> println "$i: $it"} + +//맵에 키가 포함되어 있는지 평가합니다. +assert devMap.containsKey('name') + +//맵에 값이 포함되어 있는지 평가합니다. +assert devMap.containsValue('Roberto') + +//맵의 키 가져오기 +println devMap.keySet() + +//맵의 값 가져오기 +println devMap.values() + +/* + Groovy Beans + + GroovyBeans는 JavaBeans이지만 훨씬 간단한 구문을 사용합니다. + + Groovy가 바이트코드로 컴파일될 때 다음 규칙이 사용됩니다. + + * 이름이 액세스 수정자(public, private 또는 protected)로 선언되면 필드가 생성됩니다. + + * 액세스 수정자 없이 선언된 이름은 public getter 및 setter가 있는 private 필드를 생성합니다(즉, 속성). + + * 속성이 final로 선언되면 private 필드가 final로 생성되고 setter가 생성되지 않습니다. + + * 속성을 선언하고 자신의 getter 또는 setter를 선언할 수도 있습니다. + + * 속성과 동일한 이름의 필드를 선언할 수 있으며, 속성은 해당 필드를 사용합니다. + + * private 또는 protected 속성을 원하면 자신의 getter 또는 setter를 제공해야 하며, 이는 private 또는 protected로 선언되어야 합니다. + + * 클래스 내에서 속성에 액세스하면 속성이 정의된 클래스 내에서 컴파일 타임에 암시적 또는 명시적 this(예: this.foo 또는 단순히 foo)로 액세스하면 Groovy는 getter 및 setter를 거치지 않고 필드에 직접 액세스합니다. + + * 명시적 또는 암시적 foo를 사용하여 존재하지 않는 속성에 액세스하면 Groovy는 메타 클래스를 통해 속성에 액세스하며, 이는 런타임에 실패할 수 있습니다. + +*/ + +class Foo { + // 읽기 전용 속성 + final String name = "Roberto" + + // public getter 및 protected setter가 있는 읽기 전용 속성 + String language + protected void setLanguage(String language) { this.language = language } + + // 동적으로 유형이 지정된 속성 + def lastName +} + +/* + 선택적 매개변수가 있는 메서드 +*/ + +// 메서드는 매개변수에 대한 기본값을 가질 수 있습니다. +def say(msg = 'Hello', name = 'world') { + "$msg $name!" +} + +// 3가지 다른 방식으로 호출할 수 있습니다. +assert 'Hello world!' == say() +// 기본값이 있는 가장 오른쪽 매개변수가 먼저 제거됩니다. +assert 'Hi world!' == say('Hi') +assert 'learn groovy!' == say('learn', 'groovy') + +/* + 논리적 분기 및 반복 +*/ + +//Groovy는 일반적인 if - else 구문을 지원합니다. +def x = 3 + +if(x==1) { + println "One" +} else if(x==2) { + println "Two" +} else { + println "X greater than Two" +} + +//Groovy는 삼항 연산자도 지원합니다: +def y = 10 +def x = (y > 1) ? "worked" : "failed" +assert x == "worked" + +//Groovy는 'Elvis 연산자'도 지원합니다! +//삼항 연산자를 사용하는 대신: + +displayName = user.name ? user.name : 'Anonymous' + +//다음과 같이 작성할 수 있습니다: +displayName = user.name ?: 'Anonymous' + +//For 루프 +//범위를 반복합니다. +def x = 0 +for (i in 0 .. 30) { + x += i +} + +//목록을 반복합니다. +x = 0 +for( i in [5,3,2,1] ) { + x += i +} + +//배열을 반복합니다. +array = (0..20).toArray() +x = 0 +for (i in array) { + x += i +} + +//맵을 반복합니다. +def map = ['name':'Roberto', 'framework':'Grails', 'language':'Groovy'] +x = "" +for ( e in map ) { + x += e.value + x += " " +} +assert x.equals("Roberto Grails Groovy ") + +/* + 연산자 + + Groovy가 지원하는 일반적인 연산자 목록에 대한 연산자 오버로딩: + http://www.groovy-lang.org/operators.html#Operator-Overloading + + 유용한 groovy 연산자 +*/ +//확산 연산자: 집계 개체의 모든 항목에 대해 작업을 호출합니다. +def technologies = ['Groovy','Grails','Gradle'] +technologies*.toUpperCase() // = to technologies.collect { it?.toUpperCase() } + +//안전 탐색 연산자: NullPointerException을 피하는 데 사용됩니다. +def user = User.get(1) +def username = user?.username + + +/* + 클로저 + Groovy 클로저는 "코드 블록" 또는 메서드 포인터와 같습니다. 정의된 다음 나중에 실행되는 코드 조각입니다. + + 자세한 정보: http://www.groovy-lang.org/closures.html +*/ +//예: +def clos = { println "Hello World!" } + +println "Executing the Closure:" +clos() + +//클로저에 매개변수 전달 +def sum = { a, b -> println a+b } +sum(2,4) + +//클로저는 매개변수 목록에 나열되지 않은 변수를 참조할 수 있습니다. +def x = 5 +def multiplyBy = { num -> num * x } +println multiplyBy(10) + +// 단일 인수를 사용하는 클로저가 있는 경우 클로저의 매개변수 정의를 생략할 수 있습니다. +def clos = { print it } +clos( "hi" ) + +/* + Groovy는 클로저 결과를 메모할 수 있습니다. +*/ +def cl = {a, b -> + sleep(3000) // 시간 소모적인 처리 시뮬레이션 + a + b +} + +mem = cl.memoize() + +def callClosure(a, b) { + def start = System.currentTimeMillis() + mem(a, b) + println "Inputs(a = $a, b = $b) - took ${System.currentTimeMillis() - start} msecs." +} + +callClosure(1, 2) +callClosure(1, 2) +callClosure(2, 3) +callClosure(2, 3) +callClosure(3, 4) +callClosure(3, 4) +callClosure(1, 2) +callClosure(2, 3) +callClosure(3, 4) + +/* + Expando + + Expando 클래스는 동적 빈이므로 속성을 추가하고 이 클래스의 인스턴스에 클로저를 메서드로 추가할 수 있습니다. + + http://mrhaki.blogspot.mx/2009/10/groovy-goodness-expando-as-dynamic-bean.html +*/ + def user = new Expando(name:"Roberto") + assert 'Roberto' == user.name + + user.lastName = 'Pérez' + assert 'Pérez' == user.lastName + + user.showInfo = { out -> + out << "Name: $name" + out << ", Last name: $lastName" + } + + def sw = new StringWriter() + println user.showInfo(sw) + + +/* + 메타프로그래밍 (MOP) +*/ + +//ExpandoMetaClass를 사용하여 동작 추가 +String.metaClass.testAdd = { + println "we added this" +} + +String x = "test" +x?.testAdd() + +//메서드 호출 가로채기 +class Test implements GroovyInterceptable { + def sum(Integer x, Integer y) { x + y } + + def invokeMethod(String name, args) { + System.out.println "Invoke method $name with args: $args" + } +} + +def test = new Test() +test?.sum(2,3) +test?.multiply(2,3) + +//Groovy는 속성 해결 시도를 처리하기 위해 propertyMissing을 지원합니다. +class Foo { + def propertyMissing(String name) { name } +} +def f = new Foo() + +assertEquals "boo", f.boo + +/* + TypeChecked 및 CompileStatic + Groovy는 본질적으로 동적 언어이며 항상 그럴 것이지만 TypeChecked 및 CompileStatic을 지원합니다. + + 자세한 정보: http://www.infoq.com/articles/new-groovy-20 +*/ +//TypeChecked +import groovy.transform.TypeChecked + +void testMethod() {} + +@TypeChecked +void test() { + testMeethod() + + def name = "Roberto" + + println naameee + +} + +//또 다른 예: +import groovy.transform.TypeChecked + +@TypeChecked +Integer test() { + Integer num = "1" + + Integer[] numbers = [1,2,3,4] + + Date date = numbers[1] + + return "Test" + +} + +//CompileStatic 예: +import groovy.transform.CompileStatic + +@CompileStatic +int sum(int x, int y) { + x + y +} + +assert sum(2,5) == 7 +``` + +## 추가 자료 + +[Groovy 문서](http://www.groovy-lang.org/documentation.html) + +[Groovy 웹 콘솔](http://groovyconsole.appspot.com/) + +[Groovy 사용자 그룹 가입](http://www.groovy-lang.org/usergroups.html) + +## 도서 + +* [Groovy Goodness](https://leanpub.com/groovy-goodness-notebook) +* [Groovy in Action](http://manning.com/koenig2/) +* [Programming Groovy 2: Dynamic Productivity for the Java Developer](http://shop.oreilly.com/product/9781937785307.do) \ No newline at end of file diff --git a/ko/hack.md b/ko/hack.md new file mode 100644 index 0000000000..cb730b067f --- /dev/null +++ b/ko/hack.md @@ -0,0 +1,414 @@ +--- +name: Hack +contributors: + - ["Andrew DiMola", "https://github.com/AndrewDiMola"] + - ["Stephen Holdaway", "https://github.com/stecman"] + - ["David Lima", "https://github.com/davelima"] +filename: learnhack.hh +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +[Hack](https://hacklang.org/)은 정적 타입 검사와 같은 안전 기능이 내장되어 있으면서도 코드를 빠르게 작성할 수 있게 해줍니다. + +Hack 코드를 실행하려면 오픈 소스 가상 머신인 [HHVM을 설치](https://docs.hhvm.com/hhvm/installation/introduction)하십시오. + +```php +/* ================================== + * 문서 읽기! + * ================================== + */ + +/* Hack 언어에 대한 자세한 내용은 다음을 참조하십시오: + * - Hack 정보: https://hacklang.org/ + * - 문서: https://docs.hhvm.com/hack/ + */ + +/* ================================== + * PHP에 대한 참고 사항 + * ================================== + */ + +// Hack 언어는 PHP의 상위 집합으로 시작했습니다. +// 그 이후로 언어는 (대부분) 분기되었습니다. +// 더 이상 권장되지 않는 .php 확장자를 만날 수 있습니다. + +/* ================================== + * 주석 + * ================================== + */ + +// Hack에는 한 줄 주석이 있습니다... + +/* 여러 줄 주석... + * + */ + +/** + * ... 그리고 문서 주석을 위한 특별한 구문이 있습니다. + * + * 문서 주석을 사용하여 정의, 함수, 클래스 또는 메서드의 목적을 요약하십시오. + */ + +/* ================================== + * 네임스페이스 + * ================================== + */ + +// 네임스페이스에는 클래스, 인터페이스, 트레이트, 함수 및 상수의 정의가 포함됩니다. + +namespace LearnHackinYMinutes { + + /* ================================== + * 유형 + * ================================== + */ + + function demo_hack_types(): void { + + // Hack에는 bool, int, float, string 및 null의 다섯 가지 기본 유형이 있습니다. + $is_helpful = true; // bool + $int_value = 10; // int + $precise_value = 2.0; // float + $hello_world = "Hello World!"; // string + $null_string = null; // null + + // shape 키워드를 사용하여 필드 이름 및 값 시리즈로 `shape`을 만듭니다. + $my_point = shape('x' => -3, 'y' => 6, 'visible' => true); + + // tuple 키워드를 사용하여 두 개 이상의 유형을 값으로 하는 시리즈로 `tuple`을 만듭니다. + $apple_basket = tuple("apples", 25); // 다른 유형도 괜찮습니다. + + // `arraykey`를 사용하여 정수 또는 문자열을 나타냅니다. + $the_answer = 42; + $is_answer = process_key($the_answer); + + // 마찬가지로 `num`은 int 또는 float를 나타냅니다. + $lucky_number = 7; + $lucky_square = calculate_square($lucky_number); + } + + function process_key(arraykey $the_answer): bool { + if ($the_answer is int) { + return true; + } else { + return false; + } // true + } + + function calculate_square(num $arg)[]: float { + return ((float)$arg * $arg); + } + + // 열거형은 int 또는 string(Arraykey로) 또는 다른 열거형 값으로 제한됩니다. + enum Permission: string { + Read = 'R'; + Write = 'W'; + Execute = 'E'; + Delete = 'D'; + } + + // 반면에 열거형 클래스는 모든 값 유형일 수 있습니다! + enum class Random: mixed { + int X = 42; + string S = 'foo'; + } + + /* ================================== + * HACK 배열 + * ================================== + */ + + // 다음 줄은 `C\` 네임스페이스의 함수를 사용할 수 있게 합니다. + use namespace HH\Lib\C; // `C` 라이브러리는 컨테이너에서 작동합니다. + + function demo_hack_arrays(): void { + + // vec: 정렬됨 + $v = vec[1, 2, 3]; + $letters = vec['a', 'b', 'c']; + + $letters[0]; // 'a' 반환 + $letters[] = 'd'; // 'd' 추가 + + // `inout`은 참조 전달 동작을 제공합니다. + C\pop_back(inout $letters); // 'd' 제거 + C\pop_front(inout $letters); // 'a' 제거 + + // keyset: 정렬됨, 중복 없음 + $k = keyset[1, 2, 3]; // 값은 int 또는 string이어야 합니다. + $colors = keyset['red', 'blue', 'green']; + + // keyset 키는 해당 값과 동일합니다. + $colors['blue']; // 'blue' 반환. + + $colors[] = 'yellow'; // 'yellow' 추가 + unset($colors['red']); // 'red' 제거 + + // dict: 정렬됨, 키-값 기준 + $d = dict['a' => 1, 'b' => 3]; // 키는 int 또는 string이어야 합니다. + $alphabet = dict['a' => 1, 'b' => 2]; + + $alphabet['a']; // 'a'에서 인덱싱하면 `1` 반환 + $alphabet['c'] = 3; // `c => 3`의 새 키-값 쌍 추가 + + unset($alphabet['b']); // 'b' 제거 + } + + /* ================================== + * HACK 표준 라이브러리 (HSL) + * ================================== + */ + + // Hack 표준 라이브러리는 Hack 언어를 위한 함수 및 클래스 집합입니다. + // 네임스페이스 사용 선언은 이상적으로 파일 상단에 있지만 교육 목적으로 여기에 배치됩니다. + + use namespace HH\Lib\Str; // `Str` 라이브러리는 문자열에서 작동합니다. + + function demo_hack_standard_library(): void { + + $letters = vec['a', 'b', 'c']; + $colors = keyset['red', 'blue', 'green']; + $alphabet = dict['a' => 1, 'b' => 2]; + + C\contains($letters, 'c'); // 값 확인; 'true' 반환 + C\contains($colors, 'purple'); // 값 확인; 'false' 반환 + C\contains_key($alphabet, 'a'); // 키 확인; 'true' 반환 + C\contains($alphabet, 'd'); // 값 확인; 'false' 반환 + + Str\length("foo"); // `3` 반환 + Str\join(vec['foo', 'bar', 'baz'], '!'); // `foo!bar!baz` 반환 + } + + /* ================================== + * HELLO WORLD! + * ================================== + */ + + use namespace HH\Lib\IO; // `IO` 라이브러리는 입출력을 위한 표준 API입니다. + + <<__EntryPoint>> // 일반적인 진입/주 함수에 필요한 속성 + async function main(): Awaitable< + void, + > { // 'main'으로 명명할 필요 없음 / 비동기 함수임 + await IO\request_output()->writeAllAsync( + "Hello World!\n", + ); // 'Hello World' 인쇄! + } + + /* ================================== + * 함수 + * ================================== + */ + + // 함수는 전역적으로 정의됩니다. + // 함수가 클래스에 정의되면 함수를 메서드라고 합니다. + + // 함수에는 반환 유형(여기서는 `int`)이 있으며 + // 해당 유형의 값을 반환하거나 void 반환 유형 주석이 사용된 경우 값을 반환하지 않아야 합니다. + + function add_one(int $x): int { + return $x + 1; + } + + // 함수에는 정의된 기본값도 있을 수 있습니다. + function add_value(int $x, int $y = 1): int { + return $x + $y; + } + + // 함수는 가변적일 수 있습니다(인수 길이가 지정되지 않음). + function sum_ints(int $val, int ...$vals): int { + $result = $val; + + foreach ($vals as $v) { + $result += $v; + } + return $result; + } + + // 함수는 익명일 수도 있습니다(`==>` 화살표로 정의됨). + // $f = (int $x): int ==> $x + 1; + + /* ================================== + * 파이프 연산자 + * ================================== + */ + + // 파이프 연산자 `|>`는 왼쪽 표현식의 결과를 평가하고 + // 결과를 미리 정의된 파이프 변수인 `$$`에 저장합니다. + + use namespace HH\Lib\Vec; + + function demo_pipe_operator(): void { + + Vec\sort(Vec\map(vec[2, 1, 3], $a ==> $a * $a)); // vec[1,4,9] + + // 동일한 결과이지만 파이프 연산자와 파이프 변수를 사용합니다: + $x = vec[2, 1, 3] + |> Vec\map($$, $a ==> $a * $a) // 값이 vec[2,1,3]인 $$ + |> Vec\sort($$); // 값이 vec[4,1,9]인 $$ + } + + /* ================================== + * 속성 + * ================================== + */ + + // Hack은 런타임 또는 정적 타입 검사 동작을 변경할 수 있는 내장 속성을 제공합니다. + // 예를 들어, "Hello World!" 예제에서 `__EntryPoint` 속성을 이전에 사용했습니다. + + // 또 다른 예로, `__Memoize`는 함수의 결과를 캐시합니다. + <<__Memoize>> + async function do_expensive_task(): Awaitable { + $site_contents = await \HH\Asio\curl_exec("http://hacklang.org"); + return $site_contents; + } + + /* ================================== + * 컨텍스트 + * ================================== + */ + + // Hack 함수는 다른 컨텍스트 및 기능에 연결됩니다. + // 컨텍스트는 기능 그룹, 즉 권한 그룹입니다. + + // 허용된 컨텍스트(및 기능)를 선언하려면 컨텍스트 목록 `[]`을 사용하십시오. + // 컨텍스트가 정의되지 않은 경우 함수에는 Hack의 `defaults` 컨텍스트에 정의된 권한이 포함됩니다. + + // 컨텍스트 목록이 정의되지 않았기 때문에 `defaults` 컨텍스트가 암시적으로 선언됩니다. + async function implicit_defaults_context(): Awaitable { + await IO\request_output()->writeAllAsync( + "Hello World!\n", + ); // 'Hello World' 인쇄! + } + + // 아래 함수에서 컨텍스트 목록은 `defaults` 컨텍스트를 갖도록 정의됩니다. + // 함수는 여러 컨텍스트 [context1, context2, ...]를 가질 수 있습니다. + // `defaults`에는 Hack 언어에서 정의한 대부분의 기능이 포함됩니다. + async function explicit_defaults_context()[defaults]: Awaitable { + await IO\request_output()->writeAllAsync("Hello World!\n"); + } + + // 순수 함수(기능 없음)를 만들기 위해 0개의 컨텍스트를 지정할 수도 있습니다. + async function empty_context()[]: Awaitable { + // 다음 줄은 함수에 IO 기능이 없으므로 오류입니다. + // await IO\request_output()->writeAllAsync("Hello World!\n"); + } + + /* ================================== + * 제네릭 + * ================================== + */ + + // 제네릭을 사용하면 클래스 또는 메서드를 모든 유형 집합으로 매개변수화할 수 있습니다. + // 정말 멋지네요! + + // Hack은 일반적으로 값으로 전달합니다. 참조로 전달하려면 `inout`을 사용하십시오. + function swap(inout T $input1, inout T $input2): void { + $temp = $input1; + $input1 = $input2; + $input2 = $temp; + } + + /* ================================== + * 클래스 + * ================================== + */ + + // 클래스는 기능과 상태를 함께 그룹화하는 방법을 제공합니다. + // 클래스를 정의하려면 `class` 키워드를 사용하십시오. 인스턴스화하려면 `new`를 사용하십시오. + // 다른 언어와 마찬가지로 `$this`를 사용하여 현재 인스턴스를 참조할 수 있습니다. + + class Counter { + private int $i = 0; + + public function increment(): void { + $this->i += 1; + } + + public function get(): int { + return $this->i; + } + } + + // 속성 및 메서드는 정적일 수 있습니다(인스턴스화 필요 없음). + class Person { + public static function favoriteProgrammingLanguage(): string { + return "Hack"; + } + } + + function demo_hack_classes(): void { + // `new`를 사용하여 클래스를 인스턴스화합니다. + $c1 = new Counter(); + + // 정적 속성 또는 메서드를 호출하려면 `::`를 사용하십시오. + $typical_person = tuple("Andrew", Person::favoriteProgrammingLanguage()); + } + + // 추상 클래스는 정의할 수 있지만 직접 인스턴스화할 수는 없습니다. + abstract class Machine { + public function openDoors(): void { + return; + } + public function closeDoors(): void { + return; + } + } + + /* ================================== + * 인터페이스 + * ================================== + */ + + // 클래스는 인터페이스를 통해 요구 사항 집합을 구현할 수 있습니다. + // 인터페이스는 메서드 선언 및 상수 집합입니다. + + interface Plane { + // 상수는 명명된 값입니다. 정의되면 값을 변경할 수 없습니다. + const MAX_SPEED = 300; + public function fly(): void; + } + + /* ================================== + * 트레이트 + * ================================== + */ + + // 트레이트는 속성 및 메서드 선언을 정의합니다. + // 트레이트는 재사용을 위해 코드를 추상화할 때 권장됩니다. + // 트레이트는 `use` 키워드를 통해 코드에 포함됩니다. + + trait Airplane { + // 다음 구문을 사용하여 클래스 또는 인터페이스 요구 사항을 도입합니다: + require extends Machine; // 추상 클래스 + require implements Plane; // 인터페이스 + + public function takeOff(): void { + $this->openDoors(); + $this->closeDoors(); + $this->fly(); + } + } + + class Spaceship extends Machine implements Plane { + use Airplane; + + public function fly(): void { + // 바람처럼 날아라 + } + } + + /* ================================== + * 계속 읽기! + * ================================== + */ + + /* 이것은 단순화된 가이드입니다! + * 배울 것이 훨씬 더 많습니다. 다음을 포함합니다: + * - 비동기 작업: https://docs.hhvm.com/hack/asynchronous-operations/introduction + * - 구체화된 제네릭: https://docs.hhvm.com/hack/reified-generics/reified-generics + * - XHP: https://docs.hhvm.com/hack/XHP/setup + * - ... 그리고 더! + */ +} \ No newline at end of file diff --git a/ko/haml.md b/ko/haml.md new file mode 100644 index 0000000000..b78e49d5b2 --- /dev/null +++ b/ko/haml.md @@ -0,0 +1,215 @@ +--- +name: Haml +filename: learnhaml.haml +contributors: + - ["Simon Neveu", "https://github.com/sneveu"] + - ["Vasiliy Petrov", "https://github.com/Saugardas"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Haml은 주로 Ruby와 함께 사용되는 마크업 언어로, 인라인 코드 없이 모든 웹 문서의 HTML을 깔끔하고 간단하게 설명합니다. Rails 템플릿 언어(.erb)를 사용하는 인기 있는 대안이며, 마크업에 Ruby 코드를 포함할 수 있습니다. + +코드의 들여쓰기 구조에 따라 태그를 닫아 마크업의 반복을 줄이는 것을 목표로 합니다. 그 결과 잘 구조화되고, DRY하며, 논리적이고, 읽기 쉬운 마크업이 됩니다. + +Ruby와 독립적인 프로젝트에서 Haml을 사용할 수도 있습니다. 컴퓨터에 Haml gem을 설치하고 명령줄을 사용하여 html로 변환하면 됩니다. + +```shell +$ haml input_file.haml output_file.html +``` + + +```haml +/ ------------------------------------------- +/ 들여쓰기 +/ ------------------------------------------- + +/ + 코드가 렌더링되는 방식에 들여쓰기가 중요하기 때문에, 문서 전체에서 들여쓰기는 일관되어야 합니다. 들여쓰기에 차이가 있으면 오류가 발생합니다. 두 칸 공백을 사용하는 것이 일반적이지만, 일관성만 있다면 정말로 여러분에게 달려 있습니다. + + +/ ------------------------------------------- +/ 주석 +/ ------------------------------------------- + +/ 이것이 Haml의 주석 모양입니다. + +/ + 여러 줄 주석을 작성하려면, 주석 처리된 코드를 슬래시로 감싸도록 들여쓰십시오. + +-# 이것은 조용한 주석으로, 문서에 전혀 렌더링되지 않습니다. + + +/ ------------------------------------------- +/ Html 요소 +/ ------------------------------------------- + +/ 태그를 작성하려면 퍼센트 기호 뒤에 태그 이름을 사용하십시오. +%body + %header + %nav + +/ 닫는 태그가 없습니다. 위 코드는 다음과 같이 출력됩니다. + +
+ +
+ + +/ + div 태그는 기본 요소이므로 생략할 수 있습니다. + . 또는 #을 사용하여 클래스/id만 정의할 수 있습니다. + 예를 들어 + +%div.my_class + %div#my_id + +/ 다음과 같이 작성할 수 있습니다. +.my_class + #my_id + +/ 태그에 콘텐츠를 추가하려면 선언 바로 뒤에 텍스트를 추가하십시오. +%h1 헤드라인 복사 + +/ 여러 줄 콘텐츠를 작성하려면 대신 중첩하십시오. +%p + 이것은 아마도 두 줄로 나눌 수 있는 많은 콘텐츠입니다. + +/ + 앰퍼샌드와 등호( &= )를 사용하여 html을 이스케이프할 수 있습니다. 이렇게 하면 html에 민감한 문자(&, /, :)가 html 인코딩된 해당 문자로 변환됩니다. 예를 들어 + +%p + &= "Yes & yes" + +/ 'Yes & yes'를 출력합니다. + +/ 느낌표와 등호( != )를 사용하여 html을 이스케이프 해제할 수 있습니다. +%p + != "This is how you write a paragraph tag

" + +/ 'This is how you write a paragraph tag

'를 출력합니다. + +/ CSS 클래스는 .classnames를 태그에 연결하여 추가할 수 있습니다. +%div.foo.bar + +/ 또는 Ruby 해시의 일부로 +%div{:class => 'foo bar'} + +/ 모든 태그에 대한 속성은 해시에 추가할 수 있습니다. +%a{:href => '#', :class => 'bar', :title => 'Bar'} + +/ 부울 속성의 경우 값 'true'를 할당합니다. +%input{:selected => true} + +/ 데이터 속성을 작성하려면 :data 키를 다른 해시인 값과 함께 사용하십시오. +%div{:data => {:attribute => 'foo'}} + +/ Ruby 버전 1.9 이상에서는 Ruby의 새 해시 구문을 사용할 수 있습니다. +%div{ data: { attribute: 'foo' } } + +/ 또한 HTML 스타일 속성 구문을 사용할 수 있습니다. +%a(href='#' title='bar') + +/ 그리고 두 구문을 함께 +%a(href='#'){ title: @my_class.title } + + +/ ------------------------------------------- +/ Ruby 삽입 +/ ------------------------------------------- + +/ + Ruby 값을 태그의 내용으로 출력하려면 등호 뒤에 Ruby 코드를 사용하십시오. + +%h1= book.name + +%p + = book.author + = book.publisher + + +/ html에 렌더링하지 않고 일부 Ruby 코드를 실행하려면 대신 하이픈을 사용하십시오. +- books = ['book 1', 'book 2', 'book 3'] + +/ Ruby 블록과 같이 모든 종류의 멋진 작업을 할 수 있습니다. +- books.shuffle.each_with_index do |book, index| + %h1= book + + - if book do + %p This is a book + +/ 순서 있는/순서 없는 목록 추가 +%ul + %li + =item1 + =item2 + +/ + 다시 말하지만, Ruby에 대해서도 블록에 닫는 태그를 추가할 필요가 없습니다. + 들여쓰기가 처리합니다. + +/ ------------------------------------------- +/ 부트스트랩 클래스가 있는 테이블 삽입 +/ ------------------------------------------- + +%table.table.table-hover + %thead + %tr + %th 헤더 1 + %th 헤더 2 + + %tr + %td 값1 + %td 값2 + + %tfoot + %tr + %td + 바닥글 값 + + +/ ------------------------------------------- +/ 인라인 Ruby / Ruby 보간 +/ ------------------------------------------- + +/ 일반 텍스트 줄에 Ruby 변수를 포함하려면 #{ }를 사용하십시오. +%p Your highest scoring game is #{best_game} + + +/ ------------------------------------------- +/ 필터 +/ ------------------------------------------- + +/ + 필터는 블록을 다른 필터링 프로그램에 전달하고 Haml에서 결과를 반환합니다. + 필터를 사용하려면 콜론과 필터 이름을 입력하십시오. + +/ Markdown 필터 +:markdown + # 헤더 + + **블록** *안의* 텍스트 + +/ 위 코드는 다음과 같이 컴파일됩니다. +

헤더

+ +

블록 안의 텍스트

+ +/ JavaScript 필터 +:javascript + console.log('This is inline + +/ + 다양한 유형의 필터가 있습니다(:markdown, :javascript, :coffee, :css, :ruby 등). + 또한 Haml::Filters를 사용하여 자신만의 필터를 정의할 수 있습니다. +``` + +## 추가 자료 + +- [HAML이란 무엇입니까?](http://haml.info/) - HAML 사용의 이점을 훨씬 더 잘 설명하는 좋은 소개입니다. +- [공식 문서](http://haml.info/docs/yardoc/file.REFERENCE.html) - 좀 더 깊이 들어가고 싶다면. \ No newline at end of file diff --git a/ko/haskell.md b/ko/haskell.md new file mode 100644 index 0000000000..749e633d1d --- /dev/null +++ b/ko/haskell.md @@ -0,0 +1,547 @@ +--- +name: Haskell +filename: learnhaskell.hs +contributors: + - ["Adit Bhargava", "http://adit.io"] + - ["Stanislav Modrak", "https://stanislav.gq"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Haskell은 실용적이고 순수한 함수형 프로그래밍 언어로 설계되었습니다. 모나드와 타입 시스템으로 유명하지만, 저는 그 우아함 때문에 계속해서 돌아옵니다. Haskell은 코딩을 정말 즐겁게 만듭니다. + +```haskell +-- 한 줄 주석은 두 개의 대시로 시작합니다. +{- 여러 줄 주석은 +이와 같이 블록으로 묶을 수 있습니다. +-} + +---------------------------------------------------- +-- 1. 기본 데이터 유형 및 연산자 +---------------------------------------------------- + +-- 숫자가 있습니다. +3 -- 3 + +-- 수학은 예상대로입니다. +1 + 1 -- 2 +8 - 1 -- 7 +10 * 2 -- 20 +35 / 5 -- 7.0 + +-- 나눗셈은 기본적으로 정수 나눗셈이 아닙니다. +35 / 4 -- 8.75 + +-- 정수 나눗셈 +35 `div` 4 -- 8 + +-- 부울 값은 기본형입니다. +True +False + +-- 부울 연산 +not True -- False +not False -- True +True && False -- False +True || False -- True +1 == 1 -- True +1 /= 1 -- False +1 < 10 -- True + +-- 위 예제에서 `not`은 하나의 값을 받는 함수입니다. +-- Haskell은 함수 호출에 괄호가 필요하지 않습니다... 모든 인수는 함수 뒤에 나열됩니다. 따라서 일반적인 패턴은 다음과 같습니다: +-- func arg1 arg2 arg3... +-- 자신만의 함수를 작성하는 방법에 대한 정보는 함수 섹션을 참조하십시오. + +-- 문자열 및 문자 +"This is a string." +'a' -- 문자 +'You cant use single quotes for strings.' -- 오류! + +-- 문자열을 연결할 수 있습니다. +"Hello " ++ "world!" -- "Hello world!" + +-- 문자열은 문자 목록입니다. +['H', 'e', 'l', 'l', 'o'] -- "Hello" + +-- 목록은 `!!` 연산자와 인덱스를 사용하여 인덱싱할 수 있습니다. +-- 하지만 목록은 연결 리스트이므로 이것은 O(n) 연산입니다. +"This is a string" !! 0 -- 'T' + + +---------------------------------------------------- +-- 2. 목록 및 튜플 +---------------------------------------------------- + +-- 목록의 모든 요소는 동일한 유형이어야 합니다. +-- 이 두 목록은 동일합니다: +[1, 2, 3, 4, 5] +[1..5] + +-- 범위는 다재다능합니다. +['A'..'F'] -- "ABCDEF" + +-- 범위에서 단계를 만들 수 있습니다. +[0,2..10] -- [0, 2, 4, 6, 8, 10] +[5..1] -- [] (Haskell은 기본적으로 증가합니다) +[5,4..1] -- [5, 4, 3, 2, 1] + +-- 목록 인덱싱 +[1..10] !! 3 -- 4 (0부터 시작하는 인덱싱) + +-- Haskell에서 무한 목록을 가질 수도 있습니다! +[1..] -- 모든 자연수의 목록 + +-- 무한 목록은 Haskell이 "지연 평가"를 하기 때문에 작동합니다. 이것은 Haskell이 필요할 때만 평가한다는 것을 의미합니다. 따라서 목록의 1000번째 요소를 요청하면 Haskell이 제공합니다: + +[1..] !! 999 -- 1000 + +-- 그리고 이제 Haskell은 이 목록의 1-1000 요소를 평가했습니다... 하지만 이 "무한" 목록의 나머지 요소는 아직 존재하지 않습니다! Haskell은 필요할 때까지 실제로 평가하지 않습니다. + +-- 두 목록 결합 +[1..5] ++ [6..10] + +-- 목록의 머리에 추가 +0:[1..5] -- [0, 1, 2, 3, 4, 5] + +-- 더 많은 목록 연산 +head [1..5] -- 1 +tail [1..5] -- [2, 3, 4, 5] +init [1..5] -- [1, 2, 3, 4] +last [1..5] -- 5 + +-- 목록 이해 +[x*2 | x <- [1..5]] -- [2, 4, 6, 8, 10] + +-- 조건부 +[x*2 | x <- [1..5], x*2 > 4] -- [6, 8, 10] + +-- 튜플의 모든 요소는 다른 유형일 수 있지만 튜플은 고정된 길이를 가집니다. +-- 튜플: +("haskell", 1) + +-- 쌍의 요소 액세스 (즉, 길이가 2인 튜플) +fst ("haskell", 1) -- "haskell" +snd ("haskell", 1) -- 1 + +-- 쌍 요소 액세스는 n-튜플(즉, 삼중, 사중 등)에서 작동하지 않습니다. +snd ("snd", "can't touch this", "da na na na") -- 오류! 아래 함수 참조 + +---------------------------------------------------- +-- 3. 함수 +---------------------------------------------------- +-- 두 변수를 사용하는 간단한 함수 +add a b = a + b + +-- ghci(Haskell 인터프리터)를 사용하는 경우 +-- `let`을 사용해야 합니다. 즉, +-- let add a b = a + b + +-- 함수 사용 +add 1 2 -- 3 + +-- 함수 이름을 두 인수 사이에 넣을 수도 있습니다. +-- 백틱 사용: +1 `add` 2 -- 3 + +-- 문자가 아닌 함수를 정의할 수도 있습니다! 이것은 자신만의 연산자를 정의할 수 있게 합니다! 다음은 정수 나눗셈을 수행하는 연산자입니다. +(//) a b = a `div` b +35 // 4 -- 8 + +-- 가드: 함수에서 분기하는 쉬운 방법 +fib x + | x < 2 = 1 + | otherwise = fib (x - 1) + fib (x - 2) + +-- 패턴 매칭은 비슷합니다. 여기서는 fib를 정의하는 세 가지 다른 방정식을 제공했습니다. Haskell은 왼쪽 패턴이 값과 일치하는 첫 번째 방정식을 자동으로 사용합니다. +fib 1 = 1 +fib 2 = 2 +fib x = fib (x - 1) + fib (x - 2) + +-- 튜플에서 패턴 매칭 +sndOfTriple (_, y, _) = y -- 와일드카드(_)를 사용하여 사용하지 않는 값의 이름 지정을 건너뜁니다. + +-- 목록에서 패턴 매칭. 여기서 `x`는 목록의 첫 번째 요소이고 `xs`는 목록의 나머지 부분입니다. 자신만의 map 함수를 작성할 수 있습니다: +myMap func [] = [] +myMap func (x:xs) = func x:(myMap func xs) + +-- 익명 함수는 백슬래시와 모든 인수를 사용하여 생성됩니다. +myMap (\x -> x + 2) [1..5] -- [3, 4, 5, 6, 7] + +-- 익명 함수와 함께 fold(일부 언어에서는 `inject`라고 함) 사용. foldl1은 왼쪽으로 접는 것을 의미하며, 목록의 첫 번째 값을 누산기의 초기 값으로 사용합니다. +foldl1 (\acc x -> acc + x) [1..5] -- 15 + +---------------------------------------------------- +-- 4. 더 많은 함수 +---------------------------------------------------- + +-- 부분 적용: 함수에 모든 인수를 전달하지 않으면 "부분적으로 적용"됩니다. 즉, 나머지 인수를 받는 함수를 반환합니다. + +add a b = a + b +foo = add 10 -- foo는 이제 숫자를 받아 10을 더하는 함수입니다. +foo 5 -- 15 + +-- 동일한 것을 작성하는 또 다른 방법 +foo = (10+) +foo 5 -- 15 + +-- 함수 구성 +-- 연산자 `.`는 함수를 함께 연결합니다. +-- 예를 들어, 여기서 foo는 값을 받는 함수입니다. 10을 더하고, 그 결과에 4를 곱한 다음 최종 값을 반환합니다. +foo = (4*) . (10+) + +-- 4*(10+5) = 60 +foo 5 -- 60 + +-- 우선 순위 수정 +-- Haskell에는 `$`라는 연산자가 있습니다. 이 연산자는 지정된 매개변수에 함수를 적용합니다. 가능한 가장 높은 우선 순위 10을 가지며 왼쪽 결합인 표준 함수 적용과 달리 `$` 연산자는 우선 순위 0을 가지며 오른쪽 결합입니다. 이러한 낮은 우선 순위는 오른쪽 표현식이 왼쪽 함수의 매개변수로 적용됨을 의미합니다. + +-- 이전 +even (fib 7) -- false + +-- 동일하게 +even $ fib 7 -- false + +-- 함수 구성 +even . fib $ 7 -- false + + +---------------------------------------------------- +-- 5. 타입 서명 +---------------------------------------------------- + +-- Haskell은 매우 강력한 타입 시스템을 가지고 있으며, 모든 유효한 표현식에는 타입이 있습니다. + +-- 일부 기본 타입: +5 :: Integer +"hello" :: String +True :: Bool + +-- 함수에도 타입이 있습니다. +-- `not`은 부울을 받아 부울을 반환합니다: +-- not :: Bool -> Bool + +-- 다음은 두 개의 인수를 받는 함수입니다: +-- add :: Integer -> Integer -> Integer + +-- 값을 정의할 때 그 위에 타입을 작성하는 것이 좋습니다: +double :: Integer -> Integer +double x = x * 2 + +---------------------------------------------------- +-- 6. 제어 흐름 및 If 표현식 +---------------------------------------------------- + +-- if-표현식 +haskell = if 1 == 1 then "awesome" else "awful" -- haskell = "awesome" + +-- if-표현식은 여러 줄일 수도 있으며, 들여쓰기가 중요합니다. +haskell = if 1 == 1 + then "awesome" + else "awful" + +-- case 표현식: 다음은 명령줄 인수를 구문 분석하는 방법입니다. +case args of + "help" -> printHelp + "start" -> startProgram + _ -> putStrLn "bad args" + +-- Haskell에는 루프가 없습니다. 대신 재귀를 사용합니다. +-- map은 목록의 모든 요소에 함수를 적용합니다. + +map (*2) [1..5] -- [2, 4, 6, 8, 10] + +-- map을 사용하여 for 함수를 만들 수 있습니다. +for list func = map func list + +-- 그런 다음 사용합니다. +for [0..5] $ \i -> show i + +-- 다음과 같이 작성할 수도 있습니다: +for [0..5] show + +-- filter는 조건을 만족하는 목록의 요소만 유지합니다. +filter even [1..10] -- [2, 4, 8, 10] + +-- foldl 또는 foldr을 사용하여 목록을 줄일 수 있습니다. +-- foldl <초기 값> <목록> +foldl (\x y -> 2*x + y) 4 [1,2,3] -- 43 + +-- 이것은 다음과 같습니다. +(2 * (2 * (2 * 4 + 1) + 2) + 3) + +-- foldl은 왼쪽 결합이고, foldr은 오른쪽 결합입니다. +foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16 + +-- 이것은 이제 다음과 같습니다. +(2 * 1 + (2 * 2 + (2 * 3 + 4))) + +---------------------------------------------------- +-- 7. 데이터 유형 +---------------------------------------------------- + +-- 데이터 유형은 왼쪽에 '타입 생성자'와 오른쪽에 하나 이상의 '데이터 생성자'로 선언되며, 파이프 | 기호로 구분됩니다. 이것은 합계/공용체 유형입니다. 각 데이터 생성자는 타입 생성자가 명명한 유형의 객체를 생성하는 (아마도 0항) 함수입니다. + +-- 이것은 본질적으로 열거형입니다. + +data Color = Red | Blue | Green + +-- 이제 함수에서 사용할 수 있습니다: + +say :: Color -> String +say Red = "You are Red!" +say Blue = "You are Blue!" +say Green = "You are Green!" + +-- 타입 생성자는 타입 서명에 사용되고 데이터 생성자는 함수 본문에 사용됩니다. 데이터 생성자는 주로 패턴 매칭에 사용됩니다. + +-- 다음은 두 개의 필드를 가진 전통적인 컨테이너 유형입니다. +-- 타입 선언에서 데이터 생성자는 매개변수로 타입을 받습니다. +-- 데이터 생성자는 타입 생성자와 동일한 이름을 가질 수 있습니다. +-- 이것은 타입에 단일 데이터 생성자만 있는 경우 일반적입니다. + +data Point = Point Float Float + +-- 이것은 다음과 같은 함수에서 사용할 수 있습니다: + +distance :: Point -> Point -> Float +distance (Point x y) (Point x' y') = sqrt $ dx + dy + where dx = (x - x') ** 2 + dy = (y - y') ** 2 + +-- 타입에는 인수가 있는 여러 데이터 생성자가 있을 수도 있습니다. + +data Name = Mononym String + | FirstLastName String String + | FullName String String String + +-- 더 명확하게 하기 위해 레코드 구문을 사용할 수 있습니다. + +data Point2D = CartesianPoint2D { x :: Float, y :: Float } + | PolarPoint2D { r :: Float, theta :: Float } + +myPoint = CartesianPoint2D { x = 7.0, y = 10.0 } + +-- 레코드 구문을 사용하면 접근자 함수(필드 이름)가 자동으로 생성됩니다. + +xOfMyPoint = x myPoint + +-- xOfMyPoint는 7.0과 같습니다. + +-- 레코드 구문은 또한 간단한 업데이트 형식을 허용합니다. + +myPoint' = myPoint { x = 9.0 } + +-- myPoint'는 CartesianPoint2D { x = 9.0, y = 10.0 }입니다. + +-- 타입이 레코드 구문으로 정의되었더라도 간단한 데이터 생성자처럼 선언할 수 있습니다. 이것은 괜찮습니다: + +myPoint'2 = CartesianPoint2D 3.3 4.0 + +-- `case` 표현식에서 데이터 생성자를 패턴 매칭하는 것이 유용합니다. + +distanceFromOrigin x = + case x of (CartesianPoint2D x y) -> sqrt $ x ** 2 + y ** 2 + (PolarPoint2D r _) -> r + +-- 데이터 유형에는 타입 매개변수도 있을 수 있습니다: + +data Maybe a = Nothing | Just a + +-- 이것들은 모두 Maybe 유형입니다. +Just "hello" -- `Maybe String` 유형 +Just 1 -- `Maybe Int` 유형 +Nothing -- 모든 `a`에 대한 `Maybe a` 유형 + +-- 편의를 위해 'type' 키워드로 타입 동의어를 만들 수도 있습니다. + +type String = [Char] + +-- `data` 유형과 달리 타입 동의어에는 생성자가 필요하지 않으며, 동의어 데이터 유형을 사용할 수 있는 모든 곳에서 사용할 수 있습니다. 다음과 같은 타입 동의어와 다음 타입 서명이 있는 항목이 있다고 가정해 보겠습니다. + +type Weight = Float +type Height = Float +type Point = (Float, Float) +getMyHeightAndWeight :: Person -> (Height, Weight) +findCenter :: Circle -> Point +somePerson :: Person +someCircle :: Circle +distance :: Point -> Point -> Float + +-- 다음은 의미적으로 의미가 없더라도 컴파일되고 문제 없이 실행됩니다. +-- 타입 동의어가 동일한 기본 타입으로 축소되기 때문입니다. + +distance (getMyHeightAndWeight somePerson) (findCenter someCircle) + +---------------------------------------------------- +-- 8. 타입 클래스 +---------------------------------------------------- + +-- 타입 클래스는 Haskell이 다형성을 수행하는 한 가지 방법입니다. +-- 다른 언어의 인터페이스와 유사합니다. +-- 타입 클래스는 해당 타입 클래스에 있는 모든 타입에서 작동해야 하는 함수 집합을 정의합니다. + +-- Eq 타입 클래스는 인스턴스를 서로 같음을 테스트할 수 있는 타입에 대한 것입니다. + +class Eq a where + (==) :: a -> a -> Bool + (/=) :: a -> a -> Bool + x == y = not (x /= y) + x /= y = not (x == y) + +-- 이것은 두 개의 함수 (==) 및 (/=)를 필요로 하는 타입 클래스를 정의합니다. +-- 또한 한 함수가 다른 함수로 정의될 수 있음을 선언합니다. +-- 따라서 (==) 함수 또는 (/=) 함수 중 하나만 정의하면 충분합니다. +-- 그리고 다른 하나는 타입 클래스 정의에 따라 '채워집니다'. + +-- 타입을 타입 클래스의 멤버로 만들려면 instance 키워드를 사용합니다. + +instance Eq TrafficLight where + Red == Red = True + Green == Green = True + Yellow == Yellow = True + _ == _ = False + +-- 이제 TrafficLight 객체와 함께 (==) 및 (/=)를 사용할 수 있습니다. + +canProceedThrough :: TrafficLight -> Bool +canProceedThrough t = t /= Red + +-- 타입 동의어에 대한 인스턴스 정의를 만들 수 없습니다. + +-- 함수는 타입 클래스에 타입 매개변수를 사용하여 작성할 수 있습니다. +-- 타입 대신, 함수가 타입 클래스의 기능에만 의존한다고 가정합니다. + +isEqual :: (Eq a) => a -> a -> Bool +isEqual x y = x == y + +-- x와 y는 모두 타입 매개변수 'a'로 정의되었으므로 동일한 타입이어야 합니다. +-- 타입 클래스는 타입 클래스의 다른 타입이 함께 혼합될 수 있다고 명시하지 않습니다. +-- 따라서 `isEqual Red 2`는 2가 Eq의 인스턴스인 Int이고 Red가 Eq의 인스턴스인 TrafficLight이더라도 유효하지 않습니다. + +-- 다른 일반적인 타입 클래스는 다음과 같습니다: +-- Ord는 정렬할 수 있는 타입에 대한 것으로, >, <= 등을 사용할 수 있습니다. +-- Read는 문자열 표현에서 만들 수 있는 타입에 대한 것입니다. +-- Show는 표시를 위해 문자열로 변환할 수 있는 타입에 대한 것입니다. +-- Num, Real, Integral, Fractional은 수학을 할 수 있는 타입에 대한 것입니다. +-- Enum은 단계를 거칠 수 있는 타입에 대한 것입니다. +-- Bounded는 최대 및 최소가 있는 타입에 대한 것입니다. + +-- Haskell은 타입 선언 끝에 `deriving` 키워드를 사용하여 타입을 Eq, Ord, Read, Show, Enum 및 Bounded의 일부로 자동으로 만들 수 있습니다. + +data Point = Point Float Float deriving (Eq, Read, Show) + +-- 이 경우 'instance' 정의를 만들 필요가 없습니다. + +---------------------------------------------------- +-- 9. Haskell IO +---------------------------------------------------- + +-- IO는 모나드를 설명하지 않고는 완전히 설명할 수 없지만, 시작하기에 충분히 설명하는 것은 어렵지 않습니다. + +-- Haskell 프로그램이 실행되면 `main`이 호출됩니다. 일부 타입 `a`에 대해 `IO a` 타입의 값을 반환해야 합니다. 예를 들어: + +main :: IO () +main = putStrLn $ "Hello, sky! " ++ (say Blue) +-- putStrLn은 String -> IO () 타입을 가집니다. + +-- 프로그램을 String에서 String으로의 함수로 구현할 수 있다면 IO를 수행하는 것이 가장 쉽습니다. 함수 +-- interact :: (String -> String) -> IO () +-- 일부 텍스트를 입력하고, 함수를 실행하고, 출력을 인쇄합니다. + +countLines :: String -> String +countLines = show . length . lines + +main' = interact countLines + +-- `IO ()` 타입의 값은 명령형 언어로 작성된 컴퓨터 프로그램과 마찬가지로 컴퓨터가 수행할 일련의 작업을 나타내는 것으로 생각할 수 있습니다. `do` 표기법을 사용하여 작업을 함께 연결할 수 있습니다. 예를 들어: + +sayHello :: IO () +sayHello = do + putStrLn "What is your name?" + name <- getLine -- 이것은 줄을 가져와 "name"이라는 이름을 부여합니다. + putStrLn $ "Hello, " ++ name + +-- 연습: 한 줄의 입력만 읽는 자신만의 `interact` 버전을 작성하십시오. + +-- `sayHello`의 코드는 절대 실행되지 않습니다. 실행되는 유일한 작업은 `main`의 값입니다. +-- `sayHello`를 실행하려면 위의 `main` 정의를 주석 처리하고 다음으로 바꾸십시오: +-- main = sayHello + +-- 방금 사용한 `getLine` 함수가 어떻게 작동하는지 더 잘 이해해 봅시다. 해당 타입은 다음과 같습니다: +-- getLine :: IO String +-- `IO a` 타입의 값은 실행될 때 `a` 타입의 값을 생성하는 컴퓨터 프로그램을 나타내는 것으로 생각할 수 있습니다(그 외에 수행하는 모든 작업 외에). `<-`를 사용하여 이 값을 명명하고 재사용할 수 있습니다. 또한 `IO String` 타입의 자신만의 작업을 만들 수도 있습니다: + +action :: IO String +action = do + putStrLn "This is a line. Duh" + input1 <- getLine + input2 <- getLine + -- `do` 문의 타입은 마지막 줄의 타입입니다. + -- `return`은 키워드가 아니라 단지 함수입니다. + return (input1 ++ "\n" ++ input2) -- return :: String -> IO String + +-- 이것을 `getLine`을 사용한 것처럼 사용할 수 있습니다: + +main'' = do + putStrLn "I will echo two lines!" + result <- action + putStrLn result + putStrLn "This was all, folks!" + +-- `IO` 타입은 "모나드"의 예입니다. Haskell이 IO를 수행하는 데 모나드를 사용하는 방식은 순수한 함수형 언어가 될 수 있게 합니다. 외부 세계와 상호 작용하는 모든 함수(즉, IO를 수행하는 함수)는 타입 서명에 `IO`로 표시됩니다. 이를 통해 어떤 함수가 "순수"(외부 세계와 상호 작용하거나 상태를 수정하지 않음)이고 어떤 함수가 그렇지 않은지 추론할 수 있습니다. + +-- 이것은 순수 함수를 동시에 실행하기 쉽기 때문에 강력한 기능입니다. 따라서 Haskell의 동시성은 매우 쉽습니다. + + +---------------------------------------------------- +-- 10. Haskell REPL +---------------------------------------------------- + +-- `ghci`를 입력하여 repl을 시작합니다. +-- 이제 Haskell 코드를 입력할 수 있습니다. 모든 새 값은 `let`으로 만들어야 합니다: + +let foo = 5 + +-- `:t`로 모든 값이나 표현식의 타입을 볼 수 있습니다: + +> :t foo +foo :: Integer + +-- `+`, `:` 및 `$`와 같은 연산자는 함수입니다. +-- 괄호 안에 연산자를 넣어 타입을 검사할 수 있습니다: + +> :t (:) +(:) :: a -> [a] -> [a] + +-- `:i`를 사용하여 모든 `name`에 대한 추가 정보를 얻을 수 있습니다: + +> :i (+) +class Num a where + (+) :: a -> a -> a + ... + -- ‘GHC.Num’에 정의됨 +infixl 6 + + +-- `IO ()` 타입의 모든 작업을 실행할 수도 있습니다. + +> sayHello +What is your name? +Friend! +Hello, Friend! +``` + +모나드와 타입 클래스를 포함하여 Haskell에는 훨씬 더 많은 것이 있습니다. 이것들은 Haskell에서 코딩하는 것을 매우 재미있게 만드는 큰 아이디어입니다. 마지막 Haskell 예제로 Haskell의 퀵 정렬 변형 구현을 남겨두겠습니다: + +```haskell +qsort [] = [] +qsort (p:xs) = qsort lesser ++ [p] ++ qsort greater + where lesser = filter (< p) xs + greater = filter (>= p) xs +``` + +Haskell을 설치하는 두 가지 인기 있는 방법이 있습니다: 전통적인 [Cabal 기반 설치](http://www.haskell.org/platform/)와 최신 [Stack 기반 프로세스](https://www.stackage.org/install)입니다. + +훌륭한 [Learn you a Haskell](http://learnyouahaskell.com/) (또는 [최신 커뮤니티 버전](https://learnyouahaskell.github.io/)), [Happy Learn Haskell Tutorial](http://www.happylearnhaskelltutorial.com/) 또는 [Real World Haskell](http://book.realworldhaskell.org/)에서 훨씬 더 부드러운 소개를 찾을 수 있습니다. + +``` \ No newline at end of file diff --git a/ko/haxe.md b/ko/haxe.md new file mode 100644 index 0000000000..8998984722 --- /dev/null +++ b/ko/haxe.md @@ -0,0 +1,636 @@ +--- +name: Haxe +filename: LearnHaxe3.hx +contributors: + - ["Justin Donaldson", "https://github.com/jdonaldson/"] + - ["Dan Korostelev", "https://github.com/nadako/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +[Haxe](https://haxe.org/)는 C++, C#, Swf/ActionScript, JavaScript, Java, PHP, Python, Lua, HashLink 및 Neko 바이트코드(후자의 두 가지는 Haxe 작성자가 작성함)에 대한 플랫폼 지원을 제공하는 범용 언어입니다. 참고: 이 가이드는 Haxe 버전 3용입니다. 가이드의 일부는 이전 버전에 적용될 수 있지만 다른 참조를 사용하는 것이 좋습니다. + +```haxe +/* + Y분 만에 Haxe 3 배우기에 오신 것을 환영합니다. http://www.haxe.org + 이것은 실행 가능한 튜토리얼입니다. haxe 컴파일러를 사용하여 컴파일하고 실행할 수 있습니다. + LearnHaxe.hx와 동일한 디렉토리에 있는 동안: + + $ haxe -main LearnHaxe3 --interp + + 이 단락을 둘러싼 슬래시-별표 표시를 찾으십시오. 우리는 "여러 줄 주석" 안에 있습니다. + 컴파일러에서 무시될 몇 가지 메모를 여기에 남길 수 있습니다. + + 여러 줄 주석은 haxedoc에 대한 javadoc 스타일 문서를 생성하는 데에도 사용됩니다. + 클래스, 클래스 함수 또는 클래스 변수 바로 앞에 오면 haxedoc에 사용됩니다. + */ + +// 이와 같은 이중 슬래시는 한 줄 주석을 제공합니다. + +/* + 이것은 첫 번째 실제 haxe 코드입니다. 빈 패키지를 선언합니다. + 패키지는 필요하지 않지만 코드에 대한 네임스페이스를 만들고 싶을 때 유용합니다(예: org.yourapp.ClassName). + + 패키지 선언을 생략하는 것은 빈 패키지를 선언하는 것과 같습니다. + */ +package; // 빈 패키지, 네임스페이스 없음. + +/* + 패키지는 모듈을 포함하는 디렉토리입니다. 각 모듈은 패키지에 정의된 유형을 포함하는 .hx 파일입니다. 패키지 이름(예: org.yourapp)은 소문자여야 하고 모듈 이름은 대문자여야 합니다. 모듈에는 이름이 대문자인 하나 이상의 유형이 포함됩니다. + + 예를 들어, 클래스 "org.yourapp.Foo"는 컴파일러의 작업 디렉토리 또는 클래스 경로에서 액세스할 수 있는 org/module/Foo.hx 폴더 구조를 가져야 합니다. + + 다른 파일에서 코드를 가져오는 경우 나머지 코드보다 먼저 선언해야 합니다. + Haxe는 시작하는 데 도움이 되는 많은 일반적인 기본 클래스를 제공합니다: + */ +import haxe.ds.ArraySort; + +// "*"를 사용하여 한 번에 많은 클래스/모듈을 가져올 수 있습니다. +import haxe.ds.*; + +// 정적 필드를 가져올 수 있습니다. +import Lambda.array; + +// "*"를 사용하여 모든 정적 필드를 가져올 수도 있습니다. +import Math.*; + +// "mixin"과 같이 다른 클래스의 기능을 확장할 수 있도록 특별한 방법으로 클래스를 가져올 수도 있습니다. 나중에 'using'에 대해 자세히 설명합니다. +using StringTools; + +// Typedef는 변수와 같습니다... 유형에 대한 것입니다. 코드보다 먼저 선언해야 합니다. 나중에 이에 대해 자세히 설명합니다. +typedef FooString = String; + +// Typedef는 "구조적" 유형을 참조할 수도 있습니다. 나중에 이에 대해 자세히 설명합니다. +typedef FooObject = { foo: String }; + +// 다음은 클래스 정의입니다. 파일과 동일한 이름(LearnHaxe3)을 가지므로 파일의 기본 클래스입니다. +class LearnHaxe3 { + /* + 일부 코드를 자동으로 실행하려면 정적 기본 함수에 넣고 컴파일러 인수에서 클래스를 지정해야 합니다. + 이 경우 위 컴파일러 인수에서 "LearnHaxe3" 클래스를 지정했습니다. + */ + static function main() { + /* + Trace는 haxe 표현식을 화면에 인쇄하는 기본 방법입니다. + 다른 대상에는 이를 수행하는 다른 방법이 있습니다. 예를 들어, java, c++, c# 등은 표준 출력으로 인쇄합니다. + JavaScript는 console.log로 인쇄하고 flash는 포함된 TextField로 인쇄합니다. + 모든 추적에는 기본적으로 줄 바꿈이 있습니다. + 마지막으로, 컴파일러에서 "--no-traces" 인수를 사용하여 추적이 표시되지 않도록 할 수 있습니다. + */ + trace("Hello World, with trace()!"); + + // Trace는 모든 유형의 값이나 개체를 처리할 수 있습니다. 표현식의 표현을 가능한 한 최선으로 인쇄하려고 합니다. + // "+" 연산자로 문자열을 연결할 수도 있습니다: + trace("Integer: " + 10 + " Float: " + 3.14 + " Boolean: " + true); + + // Haxe에서는 동일한 블록의 표현식을 세미콜론으로 구분해야 합니다. + // 하지만 한 줄에 두 개의 표현식을 넣을 수 있습니다: + trace('two expressions..'); trace('one line'); + + + ////////////////////////////////////////////////////////////////// + // 유형 및 변수 + ////////////////////////////////////////////////////////////////// + trace("***유형 및 변수***"); + + // "var" 키워드를 사용하여 값과 데이터 구조에 대한 참조를 저장할 수 있습니다: + var an_integer:Int = 1; + trace(an_integer + " is the value for an_integer"); + + /* + Haxe는 정적으로 유형이 지정되므로 "an_integer"는 "Int" 유형을 갖도록 선언되고 나머지 표현식은 값 "1"을 할당합니다. + 많은 경우 유형을 선언할 필요가 없습니다. 여기서 haxe 컴파일러는 another_integer의 유형이 "Int"여야 한다고 추론합니다. + */ + var another_integer = 2; + trace(another_integer + " is the value for another_integer"); + + // $type() 메서드는 컴파일러가 할당하는 유형을 인쇄합니다: + $type(another_integer); + + // 16진수로 정수를 나타낼 수도 있습니다: + var hex_integer = 0xffffff; + + /* + Haxe는 Int 및 Float 크기에 플랫폼 정밀도를 사용합니다. 또한 오버플로에 대한 플랫폼 동작을 사용합니다. + (다른 숫자 유형 및 동작은 특수 라이브러리를 사용하여 가능합니다.) + + 정수, 부동 소수점 및 부울과 같은 단순한 값 외에도 Haxe는 문자열, 배열, 목록 및 맵과 같은 일반적인 데이터 구조에 대한 표준 라이브러리 구현을 제공합니다: + */ + + // 문자열에는 큰따옴표나 작은따옴표를 사용할 수 있습니다. + var a_string = "some" + 'string'; + trace(a_string + " is the value for a_string"); + + // 문자열은 변수를 특정 위치에 삽입하여 "보간"할 수 있습니다. + // 문자열은 작은따옴표여야 하며 변수 앞에는 "$"가 와야 합니다. 표현식은 ${...}로 묶을 수 있습니다. + var x = 1; + var an_interpolated_string = 'the value of x is $x'; + var another_interpolated_string = 'the value of x + 1 is ${x + 1}'; + + // 문자열은 불변이며, 인스턴스 메서드는 문자열의 일부 또는 전체의 복사본을 반환합니다. (StringBuf 클래스도 참조하십시오). + var a_sub_string = a_string.substr(0,4); + trace(a_sub_string + " is the value for a_sub_string"); + + // 정규식도 지원되지만, 여기서는 자세히 설명할 공간이 부족합니다. + var re = ~/foobar/; + trace(re.match('foo') + " is the value for (~/foobar/.match('foo'))"); + + // 배열은 0부터 시작하고, 동적이며, 변경 가능합니다. 누락된 값은 null로 정의됩니다. + var a = new Array(); // 문자열을 포함하는 배열 + a[0] = 'foo'; + trace(a.length + " is the value for a.length"); + a[9] = 'bar'; + trace(a.length + " is the value for a.length (after modification)"); + trace(a[3] + " is the value for a[3]"); //null + + // 배열은 *제네릭*이므로 유형 매개변수로 포함하는 값을 나타낼 수 있습니다: + var a2 = new Array(); // Int 배열 + var a3 = new Array>(); // 문자열 배열의 배열. + + // 맵은 간단한 키/값 데이터 구조입니다. 키와 값은 모든 유형일 수 있습니다. + // 여기서 키는 문자열이고 값은 Int입니다: + var m = new Map(); + m.set('foo', 4); + // 배열 표기법을 사용할 수도 있습니다: + m['bar'] = 5; + trace(m.exists('bar') + " is the value for m.exists('bar')"); + trace(m.get('bar') + " is the value for m.get('bar')"); + trace(m['bar'] + " is the value for m['bar']"); + + var m2 = ['foo' => 4, 'baz' => 6]; // 대체 맵 구문 + trace(m2 + " is the value for m2"); + + // 유형 추론을 사용할 수 있습니다. Haxe 컴파일러는 유형 매개변수를 설정하는 인수를 처음 전달할 때 변수의 유형을 결정합니다. + var m3 = new Map(); + m3.set(6, 'baz'); // m3는 이제 Map입니다. + trace(m3 + " is the value for m3"); + + // Haxe는 haxe.ds 모듈에 목록, 스택 및 균형 트리와 같은 더 일반적인 데이터 구조를 가지고 있습니다. + + + ////////////////////////////////////////////////////////////////// + // 연산자 + ////////////////////////////////////////////////////////////////// + trace("***연산자***"); + + // 기본 산술 + trace((4 + 3) + " is the value for (4 + 3)"); + trace((5 - 1) + " is the value for (5 - 1)"); + trace((2 * 4) + " is the value for (2 * 4)"); + // 나눗셈은 항상 부동 소수점을 생성합니다. + trace((8 / 3) + " is the value for (8 / 3) (a Float)"); + trace((12 % 4) + " is the value for (12 % 4)"); + + // 기본 비교 + trace((3 == 2) + " is the value for 3 == 2"); + trace((3 != 2) + " is the value for 3 != 2"); + trace((3 > 2) + " is the value for 3 > 2"); + trace((3 < 2) + " is the value for 3 < 2"); + trace((3 >= 2) + " is the value for 3 >= 2"); + trace((3 <= 2) + " is the value for 3 <= 2"); + + // 표준 비트 연산자 + /* + ~ 단항 비트 보수 + << 부호 있는 왼쪽 시프트 + >> 부호 있는 오른쪽 시프트 + >>> 부호 없는 오른쪽 시프트 + & 비트 AND + ^ 비트 배타적 OR + | 비트 포함 OR + */ + + var i = 0; + trace("전/후- 증감 및 감소"); + trace(i++); // i = 1. 후-증가 + trace(++i); // i = 2. 전-증가 + trace(i--); // i = 1. 후-감소 + trace(--i); // i = 0. 전-감소 + + + ////////////////////////////////////////////////////////////////// + // 제어 구조 + ////////////////////////////////////////////////////////////////// + trace("***제어 구조***"); + + // if 문 + var j = 10; + if (j == 10) { + trace("this is printed"); + } else if (j > 10) { + trace("not greater than 10, so not printed"); + } else { + trace("also not printed."); + } + + // "삼항" if도 있습니다: + (j == 10) ? trace("equals 10") : trace("not equals 10"); + + // 마지막으로, 컴파일 타임에 작동하는 또 다른 형태의 제어 구조가 있습니다: 조건부 컴파일. +#if neko + trace('hello from neko'); +#elseif js + trace('hello from js'); +#else + trace('hello from another platform!'); +#end + + // 컴파일된 코드는 플랫폼 대상에 따라 변경됩니다. + // neko(-x 또는 -neko)용으로 컴파일하므로 neko 인사말만 받습니다. + + + trace("반복 및 반복"); + + // while 루프 + var k = 0; + while (k < 100) { + // trace(counter); // 0-99 숫자 출력 + k++; + } + + // do-while 루프 + var l = 0; + do { + trace("do 문은 항상 최소 한 번 실행됩니다."); + } while (l > 0); + + // for 루프 + // Haxe에는 C 스타일 for 루프가 없습니다. 오류가 발생하기 쉽고 필요하지 않기 때문입니다. + // 대신 Haxe에는 반복기(나중에 자세히 설명)를 사용하는 훨씬 간단하고 안전한 버전이 있습니다. + var m = [1, 2, 3]; + for (val in m) { + trace(val + " is the value for val in the m array"); + } + + // 범위에서 인덱스를 반복할 수도 있습니다. + // (범위에 대한 자세한 내용도 나중에 설명) + var n = ['foo', 'bar', 'baz']; + for (val in 0...n.length) { + trace(val + " is the value for val (an index for n)"); + } + + + trace("배열 이해"); + + // 배열 이해는 필터 및 수정을 생성하면서 배열을 반복할 수 있는 기능을 제공합니다. + var filtered_n = [for (val in n) if (val != "foo") val]; + trace(filtered_n + " is the value for filtered_n"); + + var modified_n = [for (val in n) val += '!']; + trace(modified_n + " is the value for modified_n"); + + var filtered_and_modified_n + = [for (val in n) if (val != "foo") val += "!"]; + trace(filtered_and_modified_n + + " is the value for filtered_and_modified_n"); + + + ////////////////////////////////////////////////////////////////// + // 스위치 문 (값 유형) + ////////////////////////////////////////////////////////////////// + trace("***스위치 문 (값 유형)***"); + + /* + Haxe의 스위치 문은 매우 강력합니다. 문자열 및 int와 같은 기본 값에서 작동하는 것 외에도 열거형의 일반화된 대수 데이터 유형에서도 작동할 수 있습니다(나중에 열거형에 대해 자세히 설명). + 지금은 몇 가지 기본 값 예제가 있습니다: + */ + var my_dog_name = "fido"; + var favorite_thing = ""; + switch (my_dog_name) { + case "fido" : favorite_thing = "bone"; + case "rex" : favorite_thing = "shoe"; + case "spot" : favorite_thing = "tennis ball"; + default : favorite_thing = "some unknown treat"; + // default와 동일: + // case _ : favorite_thing = "some unknown treat"; + } + // 위의 "_" 케이스는 무엇이든 일치하는 "와일드카드" 값입니다. + + trace("My dog's name is " + my_dog_name + + ", and his favorite thing is a: " + + favorite_thing); + + + ////////////////////////////////////////////////////////////////// + // 표현식 문 + ////////////////////////////////////////////////////////////////// + trace("***표현식 문***"); + + // Haxe 제어 문은 모든 문이 표현식이기도 하기 때문에 매우 강력합니다. 다음을 고려하십시오: + + // if 문 + var k = if (true) 10 else 20; + + trace("k equals ", k); // 10 출력 + + var other_favorite_thing = switch (my_dog_name) { + case "fido" : "teddy"; + case "rex" : "stick"; + case "spot" : "football"; + default : "some unknown treat"; + } + + trace("My dog's name is " + my_dog_name + + ", and his other favorite thing is a: " + + other_favorite_thing); + + + ////////////////////////////////////////////////////////////////// + // 값 유형 변환 + ////////////////////////////////////////////////////////////////// + trace("***값 유형 변환***"); + + // 문자열을 int로 쉽게 변환할 수 있습니다. + + // 문자열을 정수로 + Std.parseInt("0"); // 0 반환 + Std.parseFloat("0.4"); // 0.4 반환 + + // 정수를 문자열로 + Std.string(0); // "0" 반환 + // 문자열과 연결하면 자동으로 문자열로 변환됩니다. + 0 + ""; // "0" 반환 + true + ""; // "true" 반환 + // Std의 구문 분석에 대한 자세한 내용은 설명서를 참조하십시오. + + + ////////////////////////////////////////////////////////////////// + // 유형 처리 + ////////////////////////////////////////////////////////////////// + + /* + 앞서 언급했듯이 Haxe는 정적으로 유형이 지정된 언어입니다. 대체로 정적 타이핑은 훌륭한 것입니다. 정확한 자동 완성을 가능하게 하고 프로그램의 정확성을 철저히 확인하는 데 사용할 수 있습니다. 또한 Haxe 컴파일러는 매우 빠릅니다. + + *그러나* 컴파일러가 특정 경우에 유형 오류를 발생시키지 않고 그냥 넘어가기를 바랄 때가 있습니다. + + 이를 위해 Haxe에는 두 개의 개별 키워드가 있습니다. 첫 번째는 "Dynamic" 유형입니다: + */ + var dyn: Dynamic = "any type of variable, such as this string"; + + /* + Dynamic 변수에 대해 확실히 아는 것은 컴파일러가 더 이상 어떤 유형인지 걱정하지 않는다는 것입니다. 와일드카드 변수와 같습니다. 모든 변수 유형 대신 전달할 수 있으며 원하는 모든 변수 유형을 할당할 수 있습니다. + + 다른 더 극단적인 옵션은 "untyped" 키워드입니다: + */ + untyped { + var x:Int = 'foo'; // 이것은 옳지 않습니다! + var y:String = 4; // 광기! + } + + /* + untyped 키워드는 전체 코드 *블록*에서 작동하며, 그렇지 않으면 필요할 수 있는 모든 유형 검사를 건너뜁니다. 이 키워드는 유형 검사가 방해가 되는 제한된 조건부 컴파일 상황과 같이 매우 드물게 사용해야 합니다. + + 일반적으로 유형 검사를 건너뛰는 것은 *권장되지 않습니다*. 열거형, 상속 또는 구조적 유형 모델을 사용하여 프로그램의 정확성을 보장하는 데 도움이 됩니다. 유형 모델이 작동하지 않는다고 확신하는 경우에만 "Dynamic" 또는 "untyped"를 사용해야 합니다. + */ + + + ////////////////////////////////////////////////////////////////// + // 기본 객체 지향 프로그래밍 + ////////////////////////////////////////////////////////////////// + trace("***기본 객체 지향 프로그래밍***"); + + // FooClass의 인스턴스를 만듭니다. 이에 대한 클래스는 파일 끝에 있습니다. + var foo_instance = new FooClass(3); + + // public 변수를 정상적으로 읽습니다. + trace(foo_instance.public_any + + " is the value for foo_instance.public_any"); + + // 이 변수를 읽을 수 있습니다. + trace(foo_instance.public_read + + " is the value for foo_instance.public_read"); + // 하지만 쓸 수는 없습니다. 주석을 해제하면 오류가 발생합니다: + // foo_instance.public_read = 4; + // trace(foo_instance.public_write); // 이것도 마찬가지입니다. + + // toString 메서드를 호출합니다: + trace(foo_instance + " is the value for foo_instance"); + // 동일한 것: + trace(foo_instance.toString() + + " is the value for foo_instance.toString()"); + + // foo_instance는 "FooClass" 유형을 가지고 있고, acceptBarInstance는 BarClass 유형을 가지고 있습니다. 그러나 FooClass가 BarClass를 확장하므로 허용됩니다. + BarClass.acceptBarInstance(foo_instance); + + // 아래 클래스에는 더 고급 예제가 있으며, "example()" 메서드는 여기에서 실행합니다. + SimpleEnumTest.example(); + ComplexEnumTest.example(); + TypedefsAndStructuralTypes.example(); + UsingExample.example(); + } +} + +// 이것은 기본 LearnHaxe3 클래스의 "자식 클래스"입니다. +class FooClass extends BarClass implements BarInterface { + public var public_any:Int; // public 변수는 어디에서나 액세스할 수 있습니다. + public var public_read (default, null): Int; // public 읽기만 활성화 + public var public_write (null, default): Int; // 또는 public 쓰기만 + // 이 스타일을 사용하여 getter/setter를 활성화합니다: + public var property (get, set): Int; + + // private 변수는 클래스 외부에서 사용할 수 없습니다. + // 이에 대한 해결 방법은 @:allow를 참조하십시오. + var _private:Int; // public으로 표시되지 않은 변수는 private입니다. + + // public 생성자 + public function new(arg:Int) { + // BarClass를 확장했으므로 부모 개체의 생성자를 호출합니다: + super(); + + this.public_any = 0; + this._private = arg; + } + + // _private에 대한 getter + function get_property() : Int { + return _private; + } + + // _private에 대한 setter + function set_property(val:Int) : Int { + _private = val; + return val; + } + + // 인스턴스가 문자열로 캐스팅될 때마다 호출되는 특수 함수입니다. + public function toString() { + return _private + " with toString() method!"; + } + + // 이 클래스는 BarInterface 인터페이스를 구현하므로 이 함수가 정의되어 있어야 합니다. + public function baseFunction(x: Int) : String { + // int를 자동으로 문자열로 변환 + return x + " was passed into baseFunction!"; + } +} + +// 확장할 간단한 클래스입니다. +class BarClass { + var base_variable:Int; + public function new() { + base_variable = 4; + } + public static function acceptBarInstance(b:BarClass) {} +} + +// 구현할 간단한 인터페이스입니다. +interface BarInterface { + public function baseFunction(x:Int):String; +} + + +////////////////////////////////////////////////////////////////// +// 열거형 및 스위치 문 +////////////////////////////////////////////////////////////////// + +// Haxe의 열거형은 매우 강력합니다. 가장 간단한 형태에서 열거형은 제한된 수의 상태를 가진 유형입니다: +enum SimpleEnum { + Foo; + Bar; + Baz; +} + +// 다음은 이를 사용하는 클래스입니다: +class SimpleEnumTest { + public static function example() { + // "전체" 이름을 지정할 수 있습니다. + var e_explicit:SimpleEnum = SimpleEnum.Foo; + var e = Foo; // 하지만 추론도 작동합니다. + switch (e) { + case Foo: trace("e was Foo"); + case Bar: trace("e was Bar"); + case Baz: trace("e was Baz"); // 이 줄을 주석 처리하면 오류가 발생합니다. + } + + /* + 이것은 문자열에 대한 간단한 값 스위치와 크게 다르지 않습니다. + 그러나 *모든* 상태를 포함하지 않으면 컴파일러가 불평합니다. + 위 줄을 주석 처리하여 시도해 볼 수 있습니다. + + 열거형 스위치에 대한 기본값도 지정할 수 있습니다: + */ + switch (e) { + case Foo: trace("e was Foo again"); + default : trace("default works here too"); + } + } +} + +// 열거형은 단순한 상태보다 훨씬 더 나아가 *생성자*를 열거할 수도 있지만, 더 복잡한 열거형 예제가 필요합니다. +enum ComplexEnum { + IntEnum(i:Int); + MultiEnum(i:Int, j:String, k:Float); + SimpleEnumEnum(s:SimpleEnum); + ComplexEnumEnum(c:ComplexEnum); +} +// 참고: 위의 열거형은 자신을 포함하여 *다른* 열거형도 포함할 수 있습니다! +// 참고: 이것은 다른 언어에서 *대수 데이터 유형*이라고 하는 것입니다. + +class ComplexEnumTest { + public static function example() { + var e1:ComplexEnum = IntEnum(4); // 열거형 매개변수 지정 + // 이제 열거형을 전환하고 포함할 수 있는 모든 매개변수를 추출할 수 있습니다. + switch (e1) { + case IntEnum(x) : trace('$x was the parameter passed to e1'); + default: trace("Shouldn't be printed"); + } + + // 그 자체가 열거형인 또 다른 매개변수... 열거형 열거형? + var e2 = SimpleEnumEnum(Foo); + switch (e2){ + case SimpleEnumEnum(s): trace('$s was the parameter passed to e2'); + default: trace("Shouldn't be printed"); + } + + // 열거형 끝까지 + var e3 = ComplexEnumEnum(ComplexEnumEnum(MultiEnum(4, 'hi', 4.3))); + switch (e3) { + // 명시적으로 지정하여 특정 중첩 열거형을 찾을 수 있습니다. + case ComplexEnumEnum(ComplexEnumEnum(MultiEnum(i,j,k))) : { + trace('$i, $j, and $k were passed into this nested monster'); + } + default: trace("Shouldn't be printed"); + } + // "일반화된 대수 데이터 유형"(GADT)에 대한 자세한 내용은 왜 이것이 그렇게 훌륭한지에 대한 자세한 내용을 참조하십시오. + } +} + +class TypedefsAndStructuralTypes { + public static function example() { + // 여기서는 기본 유형 대신 typedef 유형을 사용합니다. + // 상단에서 "FooString" 유형을 "String" 유형을 의미하도록 선언했습니다. + var t1:FooString = "some string"; + + // "구조적 유형"에도 typedef를 사용할 수 있습니다. 이러한 유형은 클래스 상속이 아닌 필드 구조에 의해 정의됩니다. + // 다음은 "foo"라는 이름의 문자열 필드가 있는 익명 개체입니다: + var anon_obj = { foo: 'hi' }; + + /* + anon_obj 변수는 유형이 선언되지 않았으며 컴파일러에 따르면 익명 개체입니다. + 그러나 맨 위에서 FooObj typedef를 선언한 것을 기억하십시오. anon_obj가 해당 구조와 일치하므로 "FooObject" 유형이 예상되는 모든 곳에서 사용할 수 있습니다. + */ + var f = function(fo:FooObject) { + trace('$fo was passed in to this function'); + } + f(anon_obj); // anon_obj로 FooObject 서명 함수 호출. + + /* + typedef에는 "?"로 표시된 선택적 필드도 있을 수 있습니다. + + typedef OptionalFooObj = { + ?optionalString: String, + requiredInt: Int + } + + Typedef는 조건부 컴파일과 잘 작동합니다. 예를 들어, 파일 상단에 다음을 포함할 수 있습니다: + +#if( js ) + typedef Surface = js.html.CanvasRenderingContext2D; +#elseif( nme ) + typedef Surface = nme.display.Graphics; +#elseif( !flash9 ) + typedef Surface = flash8.MovieClip; +#elseif( java ) + typedef Surface = java.awt.geom.GeneralPath; +#end + + 그러면 모든 플랫폼에서 작업할 수 있는 단일 "Surface" 유형이 제공됩니다. + */ + } +} + +class UsingExample { + public static function example() { + /* + "using" 가져오기 키워드는 클래스의 모든 정적 메서드의 동작을 변경하는 특수 유형의 클래스 가져오기입니다. + + 이 파일에서는 문자열 유형을 처리하기 위한 여러 정적 메서드를 포함하는 "StringTools"에 "using"을 적용했습니다. + */ + trace(StringTools.endsWith("foobar", "bar") + " should be true!"); + + /* + "using" 가져오기를 사용하면 첫 번째 인수 유형이 메서드로 확장됩니다. + 무슨 뜻일까요? "endsWith"의 첫 번째 인수 유형이 "String"이므로 모든 문자열 유형에는 이제 "endsWith" 메서드가 있습니다: + */ + trace("foobar".endsWith("bar") + " should be true!"); + + /* + 이 기술은 특정 유형에 대한 많은 표현을 가능하게 하면서 단일 파일로 수정 범위를 제한합니다. + + 참고: 문자열 인스턴스는 런타임에 수정되지 않습니다. + 새로 연결된 메서드는 실제로 연결된 인스턴스의 일부가 아니며 컴파일러는 여전히 정적 메서드와 동일한 코드를 생성합니다. + */ + } +} +``` + +여기서는 Haxe가 할 수 있는 것의 표면만 긁었습니다. 모든 Haxe 기능에 대한 공식적인 개요는 [설명서](https://haxe.org/manual) 및 [API 문서](https://api.haxe.org/)를 참조하십시오. 사용 가능한 타사 Haxe 라이브러리의 포괄적인 디렉토리는 [Haxelib](https://lib.haxe.org/)를 참조하십시오. + +더 고급 주제에 대해서는 다음을 확인하는 것을 고려하십시오: + +* [추상 유형](https://haxe.org/manual/types-abstract.html) +* [매크로](https://haxe.org/manual/macro.html) +* [컴파일러 기능](https://haxe.org/manual/cr-features.html) + + +마지막으로, [Haxe 포럼](https://community.haxe.org/), IRC [#haxe on freenode](http://webchat.freenode.net/) 또는 [Haxe Gitter 채팅](https://gitter.im/HaxeFoundation/haxe)에 참여하십시오. \ No newline at end of file diff --git a/ko/hcl.md b/ko/hcl.md new file mode 100644 index 0000000000..5f692e4080 --- /dev/null +++ b/ko/hcl.md @@ -0,0 +1,327 @@ +--- +category: tool +name: HCL +contributors: + - ["Romans Malinovskis" , "http://github.com/romaninsh"] +filename: terraform.txt +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +## 소개 + +HCL(Hashicorp 구성 언어)은 Hashicorp의 도구(예: Terraform)에서 사용되는 고급 구성 언어입니다. HCL/Terraform은 클라우드 인프라를 프로비저닝하고 API를 통해 플랫폼/서비스를 구성하는 데 널리 사용됩니다. 이 문서는 HCL 0.13 구문에 중점을 둡니다. + +HCL은 선언적 언어이며 Terraform은 현재 폴더의 모든 `*.tf` 파일을 사용하므로 코드 배치 및 순서는 중요하지 않습니다. 하위 폴더는 모듈을 통해 사용할 수 있습니다. + +이 가이드는 HCL 세부 정보에 중점을 두므로 Terraform이 무엇인지 이미 알고 있어야 합니다. + +```terraform +// 최상위 HCL 파일은 기본값이 없는 변수에 대해 사용자 값을 대화식으로 묻습니다. +variable "ready" { + description = "배울 준비가 되셨습니까?" + type = bool + // default = true +} + +// 모듈 블록은 지정된 폴더에서 *.tf 파일을 참조하며, 모든 리소스 ID에 "module.learn-basics." 접두사를 효과적으로 붙입니다. +module "learn-basics" { + source = "./learn-basics" + ready_to_learn = var.ready +} + +output "knowledge" { + value = module.learn-basics.knowledge +} +``` + +## learn-basics + +```terraform +// 변수는 모듈에 자동으로 전달되지 않으며 유형이 없을 수 있습니다. +variable "ready" { +} + +// 유형을 정의하는 것이 좋습니다. 3개의 기본 유형, 3개의 컬렉션 유형 및 2개의 구조적 유형이 있습니다. 구조적 유형은 유형을 재귀적으로 정의합니다. +variable "structural-types" { + type = object({ + object: object({ + can-be-nested: bool + }), + tuple: tuple([int, string]) + }) + + default = { + object = { can-be-nested: true } + tuple = [3, "cm"] + } +} + +// 컬렉션 유형은 유형을 지정할 수 있지만 "any"일 수도 있습니다. +variable "list" { + type: list(string) + default = ["red", "green", "blue"] +} + +variable "map" { + type: map(any) + default = { + red = "#FF0000" + "green" = "#00FF00" + } +} + +variable "favourites" { + type: set + default = ["red", "blue"] +} + +// 유형이 지정되지 않았거나 스칼라가 혼합된 경우 문자열로 변환됩니다. + +// 유형 완성 기능을 위해 최신 IDE를 사용하십시오. 어떤 파일과 어떤 순서로 변수를 정의하든 상관없이 어디에서나 액세스할 수 있습니다. + +// 변수의 기본값은 표현식을 사용할 수 없지만, 이를 위해 로컬을 사용할 수 있습니다. 로컬에 대한 유형을 지정하지 않습니다. 로컬을 사용하면 다른 변수, 모듈 및 함수에서 중간 제품을 만들 수 있습니다. + +locals { + ready = var.ready ? "yes": "no" + + yaml = yamldecode(file("${path.module}/file-in-current-folder.yaml")) +} + +// 'locals' 블록은 여러 번 정의할 수 있지만 모든 변수, 리소스 및 로컬 이름은 고유해야 합니다. + +locals { + set = toset(var.map) +} + +module "more-resources" { + source = "../more-learning" + yaml-data = local.yaml +} + +// 모듈은 선택적으로 참조할 수 있는 출력을 선언할 수 있습니다(위 참조). 일반적으로 출력은 파일 하단이나 "outputs.tf"에 나타납니다. +output "knowledge" { + value = "types so far, more to come" +} +``` + +Terraform은 클라우드 "리소스"를 관리하기 위해 존재합니다. 리소스는 API 호출을 통해 생성 및 소멸될 수 있는 모든 것이 될 수 있습니다. (컴퓨팅 인스턴스, 배포, DNS 레코드, S3 버킷, SSL 인증서 또는 권한 부여). Terraform은 특정 공급업체 API를 구현하기 위해 "공급자"에 의존합니다. 예를 들어 "aws" 공급자는 AWS 클라우드 리소스를 관리하기 위한 리소스 사용을 가능하게 합니다. + +`terraform`이 호출되면(`terraform apply`) 코드를 검증하고, 메모리에 모든 리소스를 생성하고, 파일(상태 파일)에서 기존 상태를 로드하고, 현재 클라우드 API에 대해 새로 고친 다음 차이점을 계산합니다. 차이점을 기반으로 Terraform은 인프라를 HCL 정의와 일치시키기 위한 일련의 생성, 수정 또는 삭제 작업인 "계획"을 제안합니다. + +Terraform은 또한 리소스 간의 종속성을 자동으로 계산하고 올바른 생성/소멸 순서를 유지합니다. 실행 중 실패하면 전체 프로세스를 다시 시도할 수 있으며, 일반적으로 작업이 완료된 지점에서 다시 시작됩니다. + +## more-learning + +리소스를 소개할 시간입니다. + +```terraform +variable "yaml-data" { + + // config는 .yaml 파일에서 가져오므로 기술적으로는 map(any)이지만 다음과 같이 유형을 좁힐 수 있습니다: + type = map(string) +} + +// 공급자를 명시적으로 정의할 필요는 없습니다. 모두 환경 변수가 있는 합리적인 기본값을 가지고 있습니다. 공급자에 의존하는 리소스를 사용하면 투명하게 초기화됩니다(terraform init을 호출할 때). +resource "aws_s3_bucket" "bucket" { + bucket = "abc" +} + +// 공급자 별칭을 만들 수도 있습니다. +provider "aws" { + alias = "as-role" + assume_role { + role_arn = ".." + } +} + +// 그런 다음 리소스를 만드는 데 사용합니다. +resource "aws_s3_bucket_object" "test-file" { + + // 모든 리소스에는 참조할 수 있는 속성이 있습니다. 그 중 일부는 즉시 사용할 수 있으며(예: 버킷) 다른 일부는 계획이 실행되기 시작한 후에만 사용할 수 있습니다. test-file 리소스는 aws_s3_bucket.bucket 생성이 완료된 후에만 생성됩니다. + + // depends_on = aws_s3_bucket.bucket + bucket = aws_s3_bucket.bucket.bucket + key = "index.html" + content = file("${path.module}/index.html") + + // 공급자 별칭을 수동으로 지정할 수도 있습니다. + provider = aws.as-role +} + +// 각 리소스는 상태에서 "aws_s3_bucket.bucket"과 같은 ID를 받습니다. +// 모듈 내에서 리소스가 생성되면 상태 ID 앞에 module.이 붙습니다. + +module "learn-each" { + source = "../learn-each" +} + +// 이와 같이 모듈을 중첩하는 것은 최상의 방법이 아닐 수 있으며, 여기서는 설명 목적으로만 사용됩니다. +``` + +## learn-each + +Terraform은 일련의 개체를 만드는 데 유용한 몇 가지 기능을 제공합니다: + +```terraform +locals { + list = ["red", "green", "blue"] +} +resource "aws_s3_bucket" "badly-coloured-bucket" { + count = count(local.list) + bucket_prefix = "${local.list[count.index]}-" +} +// "red-" 등으로 접두사가 붙고 고유 식별자가 뒤따르는 3개의 버킷을 만듭니다. 일부 리소스는 지정되지 않은 경우 자동으로 임의의 이름을 생성합니다. 리소스(또는 이 예에서는 버킷)의 실제 이름은 속성으로 참조할 수 있습니다. + +output "red-bucket-name" { + value = aws_s3_bucket.badly-coloured-bucket[0].bucket +} + +// 버킷 리소스 ID는 "aws_s3_bucket.badly-coloured-bucket[0]"에서 2까지입니다. 목록 인덱스 요소이기 때문입니다. 그러나 목록에서 "red"를 제거하면 이제 새 ID를 갖게 되므로 모든 버킷을 다시 만듭니다. 더 나은 방법은 for_each를 사용하는 것입니다. + +resource "aws_s3_bucket" "coloured-bucket" { + // for_each는 맵과 집합만 지원합니다. + for_each = toset(local.list) + bucket_prefix = "${each.value}-" +} + +// 이 리소스의 이름은 aws_s3_bucket.coloured-bucket[red]입니다. + +output "red-bucket-name2" { + value = aws_s3_bucket.badly-coloured-bucket["red"].bucket +} + +output "all-bucket-names" { + + // 버킷 이름이 포함된 목록을 반환합니다 - "스플랫 표현식" 사용 + value = aws_s3_bucket.coloured-bucket[*].bucket +} + +// 다른 스플랫 표현식이 있습니다: +output "all-bucket-names2" { + value = [for b in aws_s3_bucket.coloured-bucket: b.bucket] +} +// 필터를 포함할 수도 있습니다. +output "filtered-bucket-names" { + value = [for b in aws_s3_bucket.coloured-bucket: + b.bucket if length(b.bucket) < 10 ] +} + +// 다음은 맵을 생성하는 몇 가지 방법입니다. {red: "red-123123.."} +output "bucket-map" { + value = { + for b in aws_s3_bucket.coloured-bucket: + trimsuffix(b.bucket_prefix, '-') + => b.bucket + } +} + +// Terraform 0.13부터는 모듈에 count/each를 사용할 수도 있습니다. + +variable "learn-functions" { + type = bool + default = true +} + +module "learn-functions" { + count = var.learn-functions ? 1: 0 + source = "../learn-functions" +} +``` + +이것은 이제 Terraform 0.13에서 작동하는 인기 있는 구문으로, 모듈을 조건부로 포함할 수 있습니다. + +## learn-functions + +Terraform은 자신만의 함수를 정의할 수 없지만, 광범위한 내장 함수 목록이 있습니다. + +```terraform +locals { + list = ["one", "two", "three"] + + upper_list = [for x in local.list : upper(x) ] // "ONE", "TWO", "THREE" + + map = {for x in local.list : x => upper(x) } // "one":"ONE", "two":"TWO", "three":"THREE" + + filtered_list = [for k, v in local.map : substr(v, 0, 2) if k != "two" ] // "ON", "TH" + + prefixed_list = [for v in local.filtered_list : "pre-${v}" ] // "pre-ON", "pre-TH" + + joined_list = join(local.upper_list,local. filtered_list) // "ONE", "TWO", "THREE", "pre-ON", "pre-TH" + + // Set은 List와 매우 유사하지만 요소 순서는 관련이 없습니다. + joined_set = toset(local.joined_list) // "ONE", "TWO", "THREE", "pre-ON", "pre-TH" + + map_again = map(slice(local.joined_list, 0, 4)) // "ONE":"TWO", "THREE":"pre-ON" +} + +// 일반적으로 목록 조작은 for_each가 있는 리소스 또는 리소스에 대한 동적 블록을 지정하는 데 유용할 수 있습니다. 이것은 일부 태그가 있는 버킷을 만듭니다: + +resource "aws_s3_bucket" "bucket" { + name = "test-bucket" + tags = local.map_again +} + +// 이것은 다음과 동일합니다: +// resource "aws_s3_bucket" "bucket" { +// name = "test-bucket" +// tags = { +// ONE = "TWO" +// THREE = "pre-ON" +// } +// } + +// 일부 리소스에는 동적 블록도 포함됩니다. 다음 예제에서는 "data" 블록을 사용하여 3개의 버킷(빨강, 녹색 및 파랑)을 조회한 다음 빨강 및 녹색 버킷에 대한 읽기 전용 액세스와 파랑 버킷에 대한 전체 액세스를 포함하는 정책을 만듭니다. + +locals { + buckets = { + red = "read-only" + green = "read-only" + blue = "full" + } + // 파일에서 버킷을 로드할 수 있습니다: + // bucket = file('bucket.json') + + actions = { + "read-only" = ["s3:GetObject", "s3:GetObjectVersion"], + "full" = ["s3:GetObject", "s3:GetObjectVersion", "s3:PutObject", "s3:PutObjectVersion"] + } + // 작업을 반복하지 않도록 작업을 조회합니다. +} + +// 함수를 사용하여 맵 키를 집합으로 변환 +data "aws_s3_bucket" "bucket" { + for_each = toset(keys(local.buckets)) + bucket = each.value +} + +// 정책에 대한 json 생성 +data "aws_iam_policy_document" "role_policy" { + statement { + effect = "Allow" + actions = [ + "ec2:*", + ] + resources = ["*"] + } + + dynamic "statement" { + for_each = local.buckets + content { + effect = "Allow" + actions = lookup(local.actions, statement.value, null) + resources = [data.aws_s3_bucket.bucket[statement.key]] + } + } +} + +// 그리고 이것은 실제로 모든 버킷에 대한 권한이 있는 AWS 정책을 만듭니다. +resource "aws_iam_policy" "policy" { + policy = data.aws_iam_policy_document.role_policy.json +} +``` + +## 추가 자료 + +- [Terraform 팁 및 요령](https://blog.gruntwork.io/terraform-tips-tricks-loops-if-statements-and-gotchas-f739bbae55f9) +- [Terraform 표현식 및 함수를 사용한 동적 출력 빌드](https://www.thegreatcodeadventure.com/building-dynamic-outputs-with-terraform-for_each-for-and-zipmap/) \ No newline at end of file diff --git a/ko/hdl.md b/ko/hdl.md new file mode 100644 index 0000000000..5a6e3a42c3 --- /dev/null +++ b/ko/hdl.md @@ -0,0 +1,193 @@ +--- +name: HDL +filename: learnhdl.hdl +contributors: + - ["Jack Smith", "https://github.com/JSmithTech2019"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +HDL(하드웨어 기술 언어)은 실제 회로의 구조/동작을 설명하는 데 사용되는 특수 언어입니다. + +회로 설계자가 하드웨어 회로를 배선하고 제작하기 전에 회로와 논리를 시뮬레이션하는 데 사용됩니다. + +HDL을 사용하면 회로 설계자가 특정 구성 요소에 연결되지 않고도 높은 수준에서 회로를 시뮬레이션할 수 있습니다. + +## 기본 구성 요소 및 언어 소개--- +이 프로그래밍 언어는 하드웨어 칩과 배선을 시뮬레이션하여 구축됩니다. 일반적인 프로그래밍 기능은 현재 배선 설계에 추가되는 특수 칩으로 대체됩니다. 모든 기본 칩은 자체 파일로 작성하고 현재 칩에서 사용하려면 가져와야 하지만, 원하는 만큼 자주 재사용할 수 있습니다. + +```verilog +// 한 줄 주석은 두 개의 슬래시로 시작합니다. + +/* + * 여러 줄 주석은 '/*'와 'star/'를 사용하여 작성할 수 있습니다. + * 이것들은 종종 주석으로 사용됩니다. + * + * 중첩될 수 없으며 첫 번째 'star/'에서 끝납니다. + */ + +//////////////////////////////////////////////////// +// 1. 칩 및 구성 요소 +//////////////////////////////////////////////////// +/* + * 다른 언어와 달리 HDL은 파일당 개별 칩(함수)을 만듭니다. + * 이것들은 이름, 입력 인수, 출력 인수 및 마지막으로 해당 특정 칩의 부품/논리로 정의됩니다. + */ + +// 참고 CHIP은 대문자이며, 칩 이름은 대문자일 필요가 없습니다. +CHIP Ex { + IN a, // 단일 비트(0 또는 1) 변수입니다. + c[16]; // 단일 비트 값의 16비트 변수 버스입니다. + + OUT out[16], // 16비트 변수 버스 출력입니다. + carry; // 단일 비트 출력 변수입니다. + + PARTS: + // 칩의 기능적 구성 요소입니다. +} + +// 줄은 세미콜론으로 끝나지만 쉼표를 사용하여 계속할 수 있습니다. 공백은 무시됩니다. + + + +//////////////////////////////////////////////////// +// 2. 입력, 출력 및 변수 +//////////////////////////////////////////////////// +/* + * 변수 및 IO는 핀/와이어로 처리되며 단일 비트의 데이터(0 또는 1)를 전달할 수 있습니다. + */ + +// 하드웨어는 저수준 0과 1에서 작동하며, 상수 높음 또는 낮음을 사용하려면 true 및 false라는 용어를 사용합니다. +a=false; // 이것은 0 값입니다. +b=true; // 이것은 1 값입니다. + +// 입력 및 출력은 단일 비트로 정의할 수 있습니다. +IN a, b; // 두 개의 단일 비트 입력을 만듭니다. + +// 각 인덱스에 단일 비트 값을 포함할 수 있는 배열 역할을 하는 버스로도 정의할 수 있습니다. +OUT c[16]; // 16비트 출력 배열을 만듭니다. + +// 버스 값은 대괄호를 사용하여 액세스할 수 있습니다. +a[0] // 버스 a의 첫 번째 인덱스 값입니다. +a[0..3] // a 버스의 처음 4개 값입니다. +// 값은 전체적으로 전달될 수도 있습니다. 예를 들어 함수 foo()가 8비트 입력 버스를 사용하고 2비트 버스를 출력하는 경우: +foo(in=a[0..7], out=c); // C는 이제 2비트 내부 버스입니다. + + +// 참고: 내부적으로 정의된 버스는 하위 버스화할 수 없습니다! +// 이러한 요소에 액세스하려면 개별적으로 출력하거나 입력하십시오: +foo(in[0]=false, in[1..7]=a[0..6], out[0]=out1, out[1]=out2); +// 그런 다음 out1 및 out2를 설계 내의 다른 회로에 전달할 수 있습니다. + + + +//////////////////////////////////////////////////// +// 하위 시스템 결합 +//////////////////////////////////////////////////// +/* + * HDL은 더 크고 복잡한 설계에 추가될 작은 "구성 요소" 칩을 사용하는 데 크게 의존합니다. 작은 구성 요소를 만든 다음 더 큰 회로에 추가하면 코드 줄 수를 줄이고 코드 재작성을 줄일 수 있습니다. + */ + +// 입력 I와 K가 모두 1인지 확인하는 함수 AND를 작성하고 있습니다. +// 이 칩을 구현하기 위해 내장된 NAND 게이트를 사용하고 단일 입력을 반전시키기 위해 사용자 지정 NOT 게이트를 설계합니다. + +// 먼저 부정(not) 칩을 구성합니다. 이 작업에는 내장된 논리적으로 완전한 게이트 NAND를 사용합니다. +CHIP Not { + IN i; // Not 게이트는 단일 비트 입력만 받습니다. + OUT o; // a의 부정 값입니다. + + PARTS: + // 내장 칩에 입력을 추가하면 NOT 출력으로 출력을 보냅니다. 이것은 주어진 값을 효과적으로 부정합니다. + Nand(a=i, b=i, out=o); +} + +// 내장된 NAND 게이트를 사용하여 실제 하드웨어 논리 칩처럼 작동하는 NOT 게이트를 구성했습니다. 이제 이 두 게이트 기본 요소를 사용하여 AND 게이트를 구성해야 합니다. + +// 두 개의 입력, 단일 출력 AND 게이트를 정의합니다: +CHIP And { + IN i, k; // 두 개의 단일 비트 입력입니다. + OUT o; // 하나의 단일 비트 출력입니다. + + PARTS: + // I와 K를 nand 게이트에 삽입하고 출력을 notOut이라는 내부 와이어에 저장합니다. + Nand(a=i,b=k,out=notOut); + + // 구성한 not 게이트를 사용하여 notOut을 반전시키고 AND 출력으로 보냅니다. + Not(in=notOut,out=o); +} + +// 쉽습니다! 이제 상위 수준 회로에서 Nand, And 및 Not 게이트를 사용할 수 있습니다. +// 이러한 저수준 구성 요소 중 다수는 HDL에 내장되어 있지만 모든 칩은 하위 모듈로 작성하여 더 큰 설계에 사용할 수 있습니다. +``` + +## 테스트 파일 +nand2tetris 하드웨어 시뮬레이터로 작업할 때 HDL을 사용하여 작성된 칩은 테스트 및 비교 파일에 대해 처리되어 시뮬레이션된 칩의 기능을 예상 출력과 비교하여 테스트합니다. 이를 위해 테스트 파일이 하드웨어 시뮬레이터에 로드되고 시뮬레이션된 하드웨어에 대해 실행됩니다. + +```verilog +// 먼저 테스트 파일이 작성된 칩이 로드됩니다. +load .hdl + +// 시뮬레이션된 칩 출력에 대한 출력 파일과 테스트할 비교 파일을 설정합니다. 또한 출력이 어떻게 보일지 지정합니다. 이 경우 두 개의 출력 열이 있으며, 각 열은 양쪽에 단일 공백으로 버퍼링되고 각 열의 중앙에 4개의 이진 값이 있습니다. +output-file .out, +compare-to .cmp, +output-list in%B1.4.1 out%B1.4.1; + +// 그런 다음 칩에 대한 입력의 초기 값을 설정합니다. 예를 들어 +set enable1 1, // 입력 enable1을 1로 설정 +set enable2 0, // 입력 enable2를 0으로 설정 + +// 클럭은 tick 및 tock을 사용하여 테스트 파일에서도 제어됩니다. Tick은 양의 펄스이고 tock은 클럭을 0으로 되돌립니다. 클럭 사이클은 입력이나 출력에 다른 변경 없이 연속으로 여러 번 실행할 수 있습니다. +tick, +tock, + +// 마지막으로 (테스트 파일에서) 첫 번째 예상 값을 출력한 다음 HDL 회로의 실제 출력의 첫 번째 줄과 비교합니다. 이 출력은 .out 파일에서 볼 수 있습니다. +output; + +// 의 예, 4비트 값을 입력으로 사용하고 해당 값에 1을 더하는 칩은 다음과 같은 테스트 코드를 가질 수 있습니다: + +// 입력 값을 0000으로 설정하고, 클럭 펄스를 보내고, cmp 파일의 출력을 실제 출력과 비교합니다. +set in %B0000, +tick, +tock, +output; + +// 입력 값을 0110으로 설정하고, 클럭 펄스를 보내고, cmp 파일의 출력을 실제 출력과 비교합니다. +set in %B0110, +tick, +tock, +output; + +// 사례 1의 예상 출력은 0001이어야 하고 사례 2는 0111을 예상합니다. 수업을 마무리하기 전에 비교 파일에 대해 조금 더 알아보겠습니다. +``` + +## 비교 파일 +이제 비교 파일을 살펴보겠습니다. 테스트 파일이 하드웨어 시뮬레이터에서 HDL 칩의 실제 출력과 비교하는 파일입니다! + +```verilog +// 위의 예제와 같이 비교 파일의 구조는 다음과 같습니다. +| in | out | +| 0000 | 0001 | +| 0110 | 0111 | + +// 테스트 사례에 지정된 입력 값이 비교 파일의 `in` 열과 동일하고 공백 버퍼가 양쪽에 1인지 확인하십시오. + +// HDL 코드의 출력이 아래 출력과 같지 않으면 테스트가 실패하고 사용자는 시뮬레이션된 칩이 올바르게 설계되지 않았음을 알게 됩니다. +| in | out | +| 0000 | 0001 | +| 0110 | 0110 | // 오류! 칩이 여기에 1을 추가하지 않았습니다. 뭔가 잘못되었습니다. +``` + +이것은 설계자가 실제 하드웨어를 제작하기 전에 칩 논리를 시뮬레이션하고 설계의 문제를 식별할 수 있으므로 매우 유용합니다. 테스트 또는 비교 파일의 오류는 거짓 양성과 더 해로운 거짓 음성을 모두 유발할 수 있으므로 테스트 생성 뒤에 있는 논리가 건전한지 확인하십시오. + + +행운을 빕니다. 즐거운 코딩 되세요! + +## 자료 + +* [Nand에서 Tetris까지](https://www.nand2tetris.org) + +## 추가 자료 + +* [하드웨어 기술 언어](https://en.wikipedia.org/wiki/Hardware_description_language) + +* [HDL 프로그래밍 기초](https://www.electronicdesign.com/products/hdl-programming-fundamentals) \ No newline at end of file diff --git a/ko/hjson.md b/ko/hjson.md new file mode 100644 index 0000000000..49365776ed --- /dev/null +++ b/ko/hjson.md @@ -0,0 +1,94 @@ +--- +name: Hjson +filename: learnhjson.hjson +contributors: + - ["MrTeferi", "https://github.com/MrTeferi"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Hjson은 [JSON](../json/)을 더 사람이 읽기 쉽게 만들려는 시도입니다. + +Hjson은 JSON에 대한 구문 확장입니다. +JSON을 대체하거나 JSON 사양 자체에 통합하려는 제안이 아닙니다. +사람이 읽고 편집한 후 JSON 데이터를 기계에 전달하기 위한 사용자 인터페이스처럼 사용하도록 만들어졌습니다. + +주요 구문 차이점을 보기 위해 예제를 살펴보겠습니다! + +``` +{ + # 주석은 완전히 지원됩니다! + + // 슬래시로도 가능합니다! + + /* + 블록 스타일 주석도 가능합니다, 멋지네요! + /* + + # 문자열은 따옴표가 필요 없습니다! + # 한 줄로 유지하십시오. + human: readable + quotes: "are fine too" + + # 쉼표도 필요하지 않습니다! + # 쉼표를 사용하는 경우 문자열에는 따옴표가 필요합니다! + object: { + name: Hjson + properties: [ + readable + exciting + fun + ] + with_commas: [ + "quoted", + "quoty", + "quote" + ] + details: ["this", "is", "fine", "too"] + } + + # 적절한 공백 처리가 있는 여러 줄 따옴표가 지원됩니다! + diary: + ''' + JSON이 더 사람이 읽기 쉬웠으면 좋겠습니다. + 내 필요에 맞는 JSON이 있다면! + 아, 잠깐.. 있습니다! Hjson이라고 합니다. + ''' + + # 백슬래시는 따옴표로 묶인 문자열에서만 이스케이프 문자로 해석됩니다. + slash: This will not have a new line\n + slash-quoted: "This will definitely have a new line\n" + + # 공백과 중요한 구두점을 혼합할 때 따옴표를 사용하십시오. + example1: "If, you're, going, to, comma in a string, use, quotes!" + example2: "Also if you want to use {} or [] or any JSON relevant punctuation!" + example3: [because, this, is, totally, BROKEN!] + example4: this is technically OK though: {}[],: + + # Hjson으로 즐겁게 작업하세요! + party-time: { + Hjson-lovers: [ + me + my mom + "my dad" + ] + Hjson-power-level: 9000 + supported: { + python: yes + java: yes + javascript: yes + c++: yes + Go: yes + C#: yes + Rust: yes + } + partial-support: ["C", "Kotlin", "Ruby", "Rust"] + } + +} +``` + +## 추가 자료 + +* [Hjson.github.io](https://hjson.github.io/) 편집기 지원, 방법 등을 포함한 주요 Hjson 사이트. +* [Hjson 패키지](https://github.com/hjson/) 다양한 애플리케이션을 위한 다양한 Hjson 패키지. \ No newline at end of file diff --git a/ko/hocon.md b/ko/hocon.md new file mode 100644 index 0000000000..9e1fd55887 --- /dev/null +++ b/ko/hocon.md @@ -0,0 +1,534 @@ +--- +name: HOCON +filename: learnhocon.conf +contributors: +- [TehBrian, 'https://tehbrian.xyz'] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +인간 최적화 구성 개체 표기법(HOCON)은 사람이 쉽게 편집할 수 있도록 설계된 구성 및 데이터 직렬화 형식입니다. + +JSON의 상위 집합이므로 모든 유효한 JSON은 유효한 HOCON이지만, 덜 독선적이라는 점에서 다릅니다. 유연하면서도 결정 가능한 구문 덕분에 결과 구성 파일은 다른 형식보다 덜 시끄럽습니다. + +또한 주석을 지원하므로 JSON보다 사용자 대면 구성에 더 적합합니다. + +``` +// // 또는 # 뒤의 모든 것은 주석입니다. 이것은 주석입니다. +# 이것도 주석입니다. + +################## +### 기본 사항 ### +################## + +# HOCON의 모든 것은 키, 값 또는 구분 기호입니다. +# : 및 =는 구분 기호입니다. 키와 값을 구분합니다. +key: value +another_key = another_value + +# 양쪽에 공백이 있거나 없는 구분 기호를 사용할 수 있습니다. +colon1:value +colon2: value +colon3 : value +equals1=value +equals2= value +equals3 = value +# 보시다시피 HOCON은 매우 제한적이지 않은 구문을 가지고 있습니다. + +# HOCON은 키 모양에 대해 독선적이지 않습니다. +THIS_IS_A_VALID_KEY: value +this-is-also-a-valid-key: value +keys can have spaces: value +or even numbers like 12345: value +"you can even quote keys if you'd like!": value + +# 키는 대소문자를 구분합니다. +unique: value 1 +UnIqUe: value 3 +UNIQUE: value 2 + +# 키, 구분 기호, 값이 뒤따르는 것을 필드라고 합니다. +this_entire_line_is: a field + +################### +### 값 유형 ### +################### + +# 값은 문자열, 숫자, 개체, 배열, 부울, null 유형일 수 있습니다. +# 단순 값은 배열 및 개체를 제외한 모든 유형의 값입니다. + +## 단순 값 ## + +quoted_string: "I like quoting my strings." +unquoted_string: I don't like quoting my strings. +# 따옴표 없는 문자열에서 사용할 수 없는 특수 문자는 다음과 같습니다: +# $ " { } [ ] : = , + # ` ^ ? ! @ * & +# 따옴표 없는 문자열은 어떤 종류의 이스케이프도 지원하지 않습니다. +# 문자열에서 이러한 특수 문자 중 하나를 사용하려면 따옴표로 묶인 문자열을 사용하십시오. +multiline_string: """This entire thing is a string! +One giant, multiline string. +You can put 'single' and "double" quotes without it being invalid.""" + +number: 123 +negative: -123 +fraction: 3.1415926536 +scientific_notation: 1.2e6 // 1.2 * 10^6 + +boolean: true # 또는 false +empty: null + +## 배열 ## + +# 배열은 값 목록을 보유합니다. + +# 배열의 값은 쉼표로 구분할 수 있습니다.. +array: [ 1, 2, 3, 4, 5 ] +fibonacci: [1,1,2,3,5,8,13] +multiples_of_5: [5, 10, 15, 20,] # 후행 쉼표를 주목하십시오. 허용됩니다. +# 또는 줄 바꿈.. +friends: [ + "Brian" + "Sophie" + "Maya" + "Sabina" +] +# 또는 둘 다! +ingredients: [ + "Egg", + "Sugar", + "Oil", + "Flour", # 후행 쉼표. 여기에서도 허용됩니다! +] +# 다시 말하지만, HOCON은 매우 자유로운 구문을 가지고 있습니다. 선호하는 스타일을 사용하십시오. + +no newline before or after bracket: ["This" + "is" + "an" + "array!"] + +# 배열은 다른 배열을 보유할 수 있습니다. +array in array: [ [1, 2, 3], ["a", "b", "c"] ] +array in array in array: [ [ [1, 2], [8, 9] ], [ ["a", "b" ], ["y", "z"] ] ] + +## 개체 ## + +# 개체는 필드를 보유합니다. + +# 배열과 마찬가지로 개체의 필드는 쉼표로 구분할 수 있습니다.. +object: { key: value, another_key: another_value } +server_connection: {ip: "127.0.0.1", port: 80} +first: {letter: a, number: 1,} # 후행 쉼표. +# 또는 줄 바꿈.. +power_grid: { + max_capacity: 15000 + current_power: 1200 +} +# 또는 둘 다! +food_colors: { + carrot: orange, + pear: green, + apple: red, + plum: purple, + banana: yellow, # 후행 쉼표. 이 성가신 것들은 어디에나 나타납니다! +} + +# 배열은 개체를 보유할 수 있습니다. +coworkers: [ + { + name: Jeff + age: 27 + }, + { + name: Henry + age: 35 + }, + { + name: Timmy + age: 12 + } +] + +# 키 뒤에 {가 오는 경우 필드 구분 기호를 생략할 수 있습니다. +no_separator { + key: value + speed_of_light: very fast + ten: 10 + + # 개체는 다른 개체를 보유할 수 있습니다. + another_object { + twenty: 20 + speed_of_sound: also pretty fast + } +} + +# 사실, 모든 HOCON 문서는 실제로 개체일 뿐입니다. +# 해당 개체를 루트 개체라고 합니다. 다른 개체와의 유일한 차이점은 문서의 맨 위와 맨 아래에 있는 중괄호를 생략할 수 있다는 것입니다. + +# 이것은 HOCON 문서를 일반 개체와 동일한 방식으로 서식 지정할 수 있음을 의미하며, 줄 바꿈 대신 쉼표로 필드를 구분하는 것을 포함합니다. + +# 또한, HOCON 문서 전체가 개체일 수 있고 일반적으로 개체이지만 배열일 수도 있습니다. 배열인 경우 문서의 맨 위와 맨 아래에 있는 여는 대괄호와 닫는 대괄호를 명시적으로 작성해야 합니다. + +###################### +### 중복 키 ### +###################### + +is_happy: false +# 중복 키가 있는 경우 새 값이 이전 값을 재정의합니다. +is_happy: true +online_users: [Jacob, Mike] +# 배열도 마찬가지입니다. +online_users: [Jacob, Mike, Henry] + +# 개체의 경우 약간 다릅니다. +my_car: { + color: blue + speed: 9001 + passengers: null + + engine: { + running: true + temperature: 137 + } +} +# 중복 키가 있고 두 값이 모두 개체인 경우 개체가 병합됩니다. +my_car: { + // 이러한 필드는 이전 개체에 추가됩니다. + nickname: "My Favorite Car" + type: 2-door sedan + + // 이 중복 키의 값이 개체가 아니므로 이전 값을 단순히 재정의합니다. + speed: 60 + // 배열도 마찬가지입니다. 병합되지 않고 재정의됩니다. + passengers: ["Nate", "Ty"] + + // 이 개체는 다른 개체와 재귀적으로 병합됩니다. + engine: { + // 이 두 필드는 이전 개체에 추가됩니다. + type: gas + oil_level: 10 + // 이 필드는 이전 값을 재정의합니다. + temperature: 179 + } +} + +# 개체 병합은 한 번에 두 개씩 수행됩니다. 즉, 처음 두 개체가 하나로 병합된 다음 해당 개체가 다음 개체와 병합되는 식입니다. + +# 이 때문에 개체 값이 있는 필드를 개체가 아닌 값으로 설정한 다음 다시 개체 값으로 설정하면 새 개체가 이전 값을 완전히 재정의합니다. + +// Null, 개체가 아닌 값은 개체를 재정의합니다. +my_car: null + +// 그런 다음 이 개체는 null을 재정의합니다. +my_car: { + nickname: "My New Car" + type: 4-door minivan + color: gray + speed: 90 + passengers: ["Ayden", "Liz"] +} + +########################### +### 값 연결 ### +########################### + +## 단순 값 연결 ## + +# 공백으로 구분된 단순 값(배열 및 개체를 제외한 모든 값 유형)은 단일 문자열로 연결됩니다. 값 사이의 공백은 유지됩니다. +number_concat: 1 2 3 12.5 -3 2e5 // "1 2 3 12.5 -3 2e5" +boolean_concat: true false true // "true false true" +null_concat: null null null // "null null null" +mixed_concat: 1 true null // "1 true null" + +# 문자열 값 연결은 따옴표로 묶인 문자열이 나타날 수 있는 모든 곳에 나타날 수 있습니다. +number_concat_in_array: [1 2, 3 4, 5 6] // ["1 2", "3 4", "5 6"] + +# 사실, 따옴표 없는 문자열은 실제로 문자열 값 연결일 뿐입니다. +unquoted_string_concat: his name is jeff // "his name is jeff" + +# 더 나아가, 따옴표 없는 문자열인 키조차도 실제로 문자열 값 연결일 뿐입니다. +this is a key: value // 키는: "this is a key" +# 다음 필드는 위 필드와 동일합니다. +"this is a key": value + +# 따옴표로 묶인 문자열도 연결할 수 있습니다. +# 이것은 나중에 대체에 대해 다룰 때 유용합니다. +quoted_string_concat: "her"" name" "is ""jenna" // "her name is jenna" +# 값 사이의 공백(또는 공백 없음)이 유지됨을 주목하십시오. + +## 배열 연결 ## + +# 공백으로 구분된 배열은 단일 배열로 병합됩니다. +array_concat: [1, 2, 3] [4, 5, 6] // [1, 2, 3, 4, 5, 6] + +# 배열은 배열이 아닌 값과 연결할 수 없습니다. +//array_concat: true [false] // 오류! +//array_concat: 1 [2] // 오류! + +## 개체 연결 ## + +# 공백으로 구분된 개체는 단일 개체로 병합됩니다. +# 병합 기능은 중복 키 개체 병합과 동일합니다. +lamp: {on: true} {color: tan} // {on: true, color: tan} + +# 배열과 마찬가지로 개체는 개체가 아닌 값과 연결할 수 없습니다. +//object_concat: true {on: false} // 오류! +//object_concat: 1 {number: 2} // 오류! + +######################## +### 경로 표현식 ### +######################## + +# 경로 표현식은 개체 그래프를 통해 경로를 작성하는 데 사용됩니다. 특정 필드로 개체를 탐색하는 것으로 생각하십시오. +# 통과할 각 개체를 요소라고 하며 각 요소는 마침표로 구분됩니다. + +country: { + city: { + neighborhood: { + house: { + name: "My House" + address: 123 Example Dr. + } + } + } +} +# 주소 경로는 다음과 같이 작성할 수 있습니다: +# country.city.neighborhood.house.address +# 국가, 도시, 이웃, 집 및 주소는 모두 요소입니다. + +# 경로 표현식은 두 곳에서 사용됩니다: 대체(잠시 후에 다룰 예정) 및 키. 맞습니다: 키는 경로 표현식일 수 있습니다. +foo: { + bar: { + baz: { + number: 12 + } + } +} +# 각 개체를 지루하게 지정하는 대신 경로 표현식을 사용할 수 있습니다. +# 다음 필드는 동일한 개체를 나타냅니다. +foo.bar.baz.number: 12 + +# 경로 표현식으로 지정된 필드 및 개체는 일반적으로 개체가 병합되는 것과 동일한 방식으로 병합됩니다. +foo.bar.baz.bool: true +// foo 개체의 값은: foo { bar { baz { number: 12, bool: true } } } + +##################### +### 대체 ### +##################### + +# 대체는 일부 경로 표현식의 특정 값을 참조합니다. +# 키나 다른 대체에 중첩된 것이 아니라 값에서만 허용됩니다. + +me: { + favorite_animal: parrots + favorite_food: cookies +} +# 대체에는 두 가지 구문이 있습니다: +# ${path_expression} 및 ${?path_expression}. +# 후자 구문은 잠시 후에 다룰 예정입니다. +my_fav_animal: ${me.favorite_animal} +my_fav_food: ${me.favorite_food} + +# 대체는 따옴표로 묶인 문자열 내에서 구문 분석되지 않습니다. 이를 해결하려면 따옴표 없는 문자열 또는 값 연결을 사용하십시오. +animal_announcement: My favorite animal is ${my_fav_animal} +// "My favorite animal is parrots" +food_announcement: "My favorite food is "${my_fav_food}"!" +// "My favorite food is cookies!" + +# 대체는 문서에서 마지막으로 구문 분석됩니다. 이 때문에 아직 정의되지 않은 키를 참조할 수 있습니다. +color_announcement: "My favorite color is" ${my_fav_color}"! +// "My favorite color is blue!" +my_fav_color: blue + +# 대체가 마지막으로 구문 분석되는 또 다른 효과는 대체가 항상 문서 전체에서 할당된 최신, 즉 마지막 값을 사용한다는 것입니다. +color: green +their_favorite_color: ${color} // orange +color: orange + +# 이것은 병합된 개체를 포함합니다. +random_object: { + number: 12 +} +the_number: ${random_object.number} // 15 +random_object: { + number: 15 +} + +############################### +### 정의되지 않은 대체 ### +############################### + +# 정의되지 않은 경로 표현식, 즉 정의된 값을 가리키지 않는 경로 표현식이 있는 ${path_expression} 구문을 사용하는 대체는 유효하지 않으므로 오류가 발생합니다. +//${does.not.exist} // 오류! + +# 그러나 ${?path_expression} 구문을 사용하는 정의되지 않은 대체는 값에 따라 다른 동작을 합니다. +request: { + # 필드의 값인 경우 필드가 생성되지 않습니다. + response: ${?does.not.exist} // 이 필드는 존재하지 않습니다. + type: HTTP +} + +request: { + # 또한 이전 값을 재정의했을 경우 이전 값은 변경되지 않습니다. + type: ${?does.not.exist} // request.type은 여전히 HTTP입니다. +} + +# 배열의 값인 경우 단순히 추가되지 않습니다. +values: [ 172, "Brian", ${?does.not.exist}, null, true, ] +// [ 172, "Brian", null, true ] + +# 단순 값 연결의 일부인 경우 빈 문자열 역할을 합니다. +final_string: "String One"${?does.not.exist}"String Two" +// "String OneString Two" + +# 배열 연결의 일부인 경우 빈 배열 역할을 합니다. +final_array: [ 1, 2, 3 ] ${?does.not.exist} [ 7, 8, 9 ] +// [ 1, 2, 3, 7, 8, 9 ] + +# 개체 연결의 일부인 경우 빈 개체 역할을 합니다. +final_object: { a: 1 } ${?does.not.exist} { c: 3 } +// { a: 1, c: 3 } + +###################################### +### 자기 참조 대체 ### +###################################### + +# 대체는 일반적으로 "앞을 내다보고" 문서에 정의된 최종 값을 사용합니다. 그러나 이것이 주기를 생성하는 경우 대체는 뒤로만 봅니다. + +# 자신을 가리키거나 결국 자신을 다시 가리키는 다른 필드를 가리키는 대체가 포함된 필드를 자기 참조 필드라고 합니다. +letters: "a b c" // "a b c" +letters: ${letters}" d" // "a b c d" +letters: ${letters}" e" // "a b c d e" + +PATH: [/bin] // [/bin] +PATH: ${PATH} [/usr/bin] // [/bin, /usr/bin] +PATH: ${PATH} [/usr/local/bin] // [/bin, /usr/bin, /usr/local/bin] + +x: "x" // "x" +y: ${x}"y" // "xy" +x: ${y}"z" // "xyz" + +########################## +### += 필드 구분 기호 ### +########################## + +# : 및 = 외에도 실제로 다른 구분 기호가 있습니다: += +# +=로 구분된 필드는 자기 참조 배열 연결을 의미합니다. +# 본질적으로 이전에 정의된 배열에 요소를 추가합니다. + +a: [1] +b: [1] +# 이 두 필드는 동일합니다. +a += 2 // [1, 2] +b: ${?b} [2] // [1, 2] + +USERS: [/usr/luke] // [/usr/luke] +USERS += /usr/devon // [/usr/luke, /usr/devon] +USERS += /usr/michael // [/usr/luke, /usr/devon, /usr/michael] + +# +=는 이전에 존재하는 배열에만 요소를 추가하므로 이전 값이 배열이 아니면 오류가 발생합니다. +OTHER_USERS: /usr/luke +//OTHER_USERS += /usr/devon // 오류! + +# 사용된 기본 대체 구문은 ${?path}가 아닌 ${path}입니다. +# ${?} 구문을 사용하면 배열 연결에서 정의되지 않은 대체가 빈 배열 역할을 한다는 것을 기억하십시오. 이 때문에 설정되는 필드가 처음에 정의되지 않은 경우 완벽하게 허용됩니다. +//z: [] // 필요 없음 +z += 3 // [3] +z += 4 // [3, 4] + +NEW_USERS += /usr/sandra // [/usr/sandra] +NEW_USERS += /usr/kennedy // [/usr/sandra, /usr/kennedy] +NEW_USERS += /usr/robin // [/usr/sandra, /usr/kennedy, /usr/robin] + +################ +### 포함 ### +################ + +# 포함을 사용하면 한 HOCON 문서를 다른 문서로 "가져올" 수 있습니다. + +# 포함 문은 따옴표 없는 문자열 "include"와 공백, 그리고 다음 중 하나인 리소스 이름으로 구성됩니다: +# - URL, 파일 이름 또는 Java 클래스 경로 리소스로 휴리스틱하게 해석되는 단일 따옴표로 묶인 문자열. +# - url(), file() 또는 classpath(), 괄호는 각각 URL, 파일 이름 또는 클래스 경로 리소스인 따옴표로 묶인 문자열을 둘러쌉니다. +# - required(), 괄호는 위 중 하나를 둘러쌉니다. +include "https://example.com/config.conf" +include "/foo/bar/config.conf" +include "config.conf" + +include url("https://example.com/config.conf") +include file("/foo/bar/config.conf") +include classpath("config.conf") + +# 포함된 파일이 존재하지 않으면 자동으로 무시되고 빈 개체인 것처럼 작동합니다. 그러나 required()로 래핑된 경우 파일을 확인할 수 없으면 구문 분석이 명시적으로 오류를 발생시킵니다. +//include required("doesnt_exist.conf") // 오류! +//include required(url("https://example.com/doesnt_exist.conf")) // 오류! +//include required(file("doesnt_exist.conf")) // 오류! +//include required(classpath("doesnt_exist.conf")) // 오류! + +# 포함 문으로 지정된 파일을 포함된 파일이라고 합니다. +# 포함 문이 포함된 파일을 포함하는 파일이라고 합니다. + +# 파일을 포함하는 것은 포함된 파일의 루트 개체 내용으로 포함 문을 직접 교체하는 것처럼 작동합니다. + +# 포함된 파일은 루트 값으로 개체를 가져야 하며 배열이 아니어야 합니다. +# 포함된 파일에 루트 값으로 배열이 있는 경우 유효하지 않으며 오류가 발생합니다. + +# 다음이 user_config.conf라는 파일에 있다고 가정합니다: +username: RandomUser1337 +auto_login: true +color_theme: dark +screensaver: { + image: usr/images/screensaver.jpg + turn_on_after: 1m +} + +# 그런 다음 해당 파일을 포함합니다. +include file("user_config.conf") + +# 이제 해당 파일의 값을 참조할 수 있습니다! +path_to_user_screensaver: ${screensaver.image} // "usr/images/screensaver.jpg" +greeting: "Welcome, "${username}"!" // "Welcome, RandomUser1337!" + +# 중복 키는 일반적으로 재정의됩니다. +status: "Auto Login: "${auto_login} // "Auto Login: true" +auto_login: false +status: "Auto Login: "${auto_login} // "Auto Login: false" + +# 개체 병합은 평소와 같습니다. +screensaver: { + // 이것은 screensaver 개체에 추가됩니다. + enable_during_day: false + // 이것은 이전 값을 재정의합니다. + turn_on_after: 30s +} + +# 포함 문은 필드 대신 나타날 수 있습니다. 필드가 나타날 수 있는 모든 곳에 포함 문이 나타날 수 있습니다. + +# 다음이 server_settings.conf라는 파일에 있다고 가정합니다: +max_connections: 10 +url: example.com +port: 80 +admin_page: { + username: admin + password: pass12345 +} + +# 그런 다음 개체 내에 중첩된 파일을 포함합니다. +websites: { + my_epic_website: { + include file("server_settings.conf") + } +} + +# 이제 server_settings.conf의 내용을 개체 my_epic_website에 직접 작성된 것처럼 참조할 수 있습니다. +server_port: ${websites.my_epic_website.port} + +the_password: "The password is: "${websites.my_epic_website.admin_page.password} +// "The password is: pass12345" + +max_conn: "Max Connections: "${websites.my_epic_website.max_connections} +// "Max Connections: 10" +``` + +### 추가 자료 + ++ [공식 HOCON 사양](https://github.com/lightbend/config/blob/master/HOCON.md) ++ [HOCON 놀이터](https://hocon-playground.tehbrian.dev) \ No newline at end of file diff --git a/ko/hq9+.md b/ko/hq9+.md new file mode 100644 index 0000000000..d49cf8160d --- /dev/null +++ b/ko/hq9+.md @@ -0,0 +1,39 @@ +--- +name: HQ9+ +filename: hq9+.txt +contributors: + - ["Alexey Nazaroff", "https://github.com/rogaven"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +HQ9+는 Cliff Biffle이 만든 농담 프로그래밍 언어입니다. 4개의 명령만 있으며 튜링 완전하지 않습니다. + +``` +다음 문자로 표시되는 4개의 명령만 있습니다. +H: "Hello, world!" 인쇄 +Q: 프로그램의 소스 코드 인쇄 (Quine) +9: "99 Bottles of Beer" 가사 인쇄 ++: 누산기에 1을 더합니다 (누산기 값에 액세스할 수 없음) +다른 모든 문자는 무시됩니다. + +좋습니다. 프로그램을 작성해 보겠습니다: + HQ9 + +결과: + Hello world! + HQ9 + +HQ9+는 매우 간단하지만 다른 언어에서는 매우 어려운 일부 작업을 수행할 수 있습니다. 예를 들어, 화면에 자신을 세 번 복사하는 프로그램은 다음과 같습니다: + QQQ + +이것은 다음을 생성합니다: + QQQ + QQQ + QQQ +``` + +이게 전부입니다. HQ9+에 대한 많은 인터프리터가 있습니다. 아래에서 그 중 하나를 찾을 수 있습니다. + ++ [온라인 인터프리터 중 하나](https://almnet.de/esolang/hq9plus.php) ++ [HQ9+ 공식 웹사이트](http://cliffle.com/esoterica/hq9plus.html) \ No newline at end of file diff --git a/ko/html.md b/ko/html.md new file mode 100644 index 0000000000..59621113a2 --- /dev/null +++ b/ko/html.md @@ -0,0 +1,161 @@ +--- +name: HTML +filename: learnhtml.txt +contributors: + - ["Christophe THOMAS", "https://github.com/WinChris"] +translators: + - ["Robert Steed", "https://github.com/robochat"] + - ["Dimitri Kokkonis", "https://github.com/kokkonisd"] + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +HTML은 하이퍼텍스트 마크업 언어의 약자입니다. + +월드 와이드 웹을 위한 페이지를 작성할 수 있는 언어입니다. +마크업 언어로, 텍스트와 데이터가 어떻게 표시되어야 하는지를 나타내는 코드를 사용하여 웹 페이지를 작성할 수 있습니다. 사실, HTML 파일은 간단한 텍스트 파일입니다. + +이 마크업은 무엇입니까? 여는 태그와 닫는 태그로 둘러싸서 페이지의 데이터를 구성하는 방법입니다. 이 마크업은 둘러싸는 텍스트에 의미를 부여하는 역할을 합니다. 다른 컴퓨터 언어와 마찬가지로 HTML에는 여러 버전이 있습니다. 여기서는 HTML5에 대해 이야기하겠습니다. + +**참고:** [codepen](http://codepen.io/pen/)과 같은 사이트에서 튜토리얼을 진행하면서 다양한 태그와 요소를 테스트하여 효과를 확인하고 언어에 익숙해질 수 있습니다. 이 문서는 주로 HTML 구문과 몇 가지 유용한 팁에 중점을 둡니다. + + +```html + + + + + + + + + + + + + My Site + + +

Hello, world!

+ + Come look at what this shows + +

This is a paragraph.

+

This is another paragraph.

+
    +
  • This is an item in a non-enumerated list (bullet list)
  • +
  • This is another item
  • +
  • And this is the last item on the list
  • +
+ + + + + + + + + + + + + + + + + + + + + + My Site + + + + + + + + +

Hello, world!

+ + + + + Come look at what this shows + + + +

This is a paragraph.

+

This is another paragraph.

+ + + +
    +
  • This is an item in a non-enumerated list (bullet list)
  • +
  • This is another item
  • +
  • And this is the last item on the list
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
First HeaderSecond Header
first row, first columnfirst row, second column
second row, first columnsecond row, second column
+``` + +## 사용법 + +HTML은 `.html` 또는 `.htm` 확장자로 끝나는 파일에 작성됩니다. 마임 유형은 `text/html`입니다. +**HTML은 프로그래밍 언어가 아닙니다.** +## 더 배우기 + +* [위키백과](https://en.wikipedia.org/wiki/HTML) +* [HTML 튜토리얼](https://developer.mozilla.org/en-US/docs/Web/HTML) +* [W3Schools](http://www.w3schools.com/html/html_intro.asp) \ No newline at end of file diff --git a/ko/httpie.md b/ko/httpie.md new file mode 100644 index 0000000000..1ef1aebcae --- /dev/null +++ b/ko/httpie.md @@ -0,0 +1,115 @@ +--- +category: tool +name: HTTPie +contributors: + - ["Adaías Magdiel", "https://github.com/AdaiasMagdiel"] +filename: learn-httpie.sh +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +HTTPie는 HTTP 서버와 쉽게 상호 작용하도록 설계된 강력한 명령줄 HTTP 클라이언트입니다. 간단하고 직관적인 인터페이스를 제공하여 개발자, 테스터 및 시스템 관리자에게 훌륭한 도구입니다. + +## 기본 사용법 + +HTTPie는 간단한 구문을 따릅니다: http [플래그] [메서드] URL [항목]. + +```bash +http GET https://api.example.com/posts +``` + +`--offline` 플래그를 사용하여 요청을 보내지 않고 인쇄할 수 있습니다. + +```bash +http --offline https://api.example.com/posts +``` + +### `localhost`에 대한 URL 바로 가기 + +HTTPie는 localhost에 대한 curl과 유사한 약어를 지원합니다. 예를 들어, `:3000`은 `http://localhost:3000`으로 확장됩니다. 포트가 생략되면 포트 80을 가정합니다. + +```bash +http :/users # http://localhost/users +http :5000/rss # http://localhost:5000/rss +``` + +### 선택적 GET 및 POST + +메서드를 지정하지 않으면 HTTPie는 다음을 사용합니다: + +- 본문 없는 요청의 경우 GET +- 본문 있는 요청의 경우 POST + +```bash +http https://api.example.com/tags # GET 태그 +http https://api.example.com/tags title="Tutorial" slug="tutorial" # 새 태그 POST +``` + +## 쿼리 문자열 매개변수 + +터미널에서 쿼리 문자열 매개변수를 수동으로 추가하는 경우 `param==value` 구문을 사용해 보십시오. & 구분 기호에 대한 셸 이스케이프를 피하고 매개변수 이름 및 값의 특수 문자를 자동으로 URL 이스케이프합니다. 이는 HTTPie가 수정하지 않는 전체 URL의 매개변수와 다릅니다. + +```bash +http https://api.example.com/search q==httpie per_page==20 +``` + +## 데이터 전송 + +JSON, 양식 데이터 또는 파일과 같은 다양한 형식으로 데이터를 보낼 수 있습니다. + +### JSON 데이터 + +```bash +http POST https://api.example.com/posts title="Hello" body="World" +``` + +### 양식 데이터 + +```bash +http -f POST https://api.example.com/submit name=John email=john@example.com +``` + +### 파일 + +```bash +http --form POST https://api.example.com/upload file@/path/to/file.txt +``` + +## 헤더 및 인증 + +HTTPie를 사용하면 헤더를 설정하고 인증을 쉽게 처리할 수 있습니다. + +### 헤더 + +```bash +http GET https://api.example.com/posts Authorization:"Bearer Token" User-Agent:"HTTPie" +``` + +### 기본 인증 + +```bash +http -a username:password GET https://api.example.com/protected +``` + +### 전달자 인증 + +```bash +https -A bearer -a token https://api.example.com/admin +``` + +## 응답 처리 + +HTTPie는 응답을 처리하기 위한 다양한 옵션을 제공합니다. + +```bash +http GET https://api.example.com/data Accept:application/json # JSON 예쁘게 인쇄 + +http GET https://api.example.com/image --output image.png # 파일에 응답 저장 + +http --follow GET https://example.com # 리디렉션 따르기 +``` + +## 추가 자료 + +- [공식 문서](https://httpie.io/docs/cli) +- [GitHub](https://github.com/httpie) \ No newline at end of file diff --git a/ko/hy.md b/ko/hy.md new file mode 100644 index 0000000000..0406d139a9 --- /dev/null +++ b/ko/hy.md @@ -0,0 +1,184 @@ +--- +name: Hy +filename: learnhy.hy +contributors: + - ["Abhishek L", "http://twitter.com/abhishekl"] + - ["Zirak", "http://zirak.me"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Hy는 Python 위에 구축된 Lisp 방언입니다. 이것은 Hy 코드를 Python의 추상 구문 트리(AST)로 변환하여 달성됩니다. 이를 통해 Hy는 네이티브 Python 코드를 호출하거나 Python이 네이티브 Hy 코드를 호출할 수 있습니다. + +```hylang +; 다른 Lisp와 마찬가지로 세미콜론 주석 + +;; S-표현식 기본 +; Lisp 프로그램은 기호 표현식 또는 sexps로 구성됩니다. +(some-function args) +; 이제 전형적인 hello world +(print "hello world") + +;; 간단한 데이터 유형 +; 모든 간단한 데이터 유형은 Python의 해당 유형과 동일합니다. +42 ; => 42 +3.14 ; => 3.14 +True ; => True +4+10j ; => (4+10j) 복소수 + +; 간단한 산술부터 시작하겠습니다. +(+ 4 1) ;=> 5 +; 다른 Lisp와 마찬가지로 연산자는 모든 인수에 적용됩니다. +(+ 4 1 2 3) ;=> 10 +(- 2 1) ;=> 1 +(* 4 2) ;=> 8 +(/ 4 1) ;=> 4 +(% 4 2) ;=> 0 모듈로 연산자 +; 거듭제곱은 Python과 같이 ** 연산자로 표시됩니다. +(** 3 2) ;=> 9 +; 중첩된 형식은 예상대로 작동합니다. +(+ 2 (* 4 2)) ;=> 10 +; 또한 논리 연산자 and or not 및 등호 등은 예상대로 작동합니다. +(= 5 4) ;=> False +(not (= 5 4)) ;=> True + +;; 변수 +; 변수는 setv를 사용하여 설정되며, 변수 이름은 ()[]{}",'`;#|를 제외한 모든 utf-8을 사용할 수 있습니다. +(setv a 42) +(setv π 3.14159) +(def *foo* 42) +;; 다른 컨테이너 데이터 유형 +; 문자열, 목록, 튜플 및 사전 +; 이것들은 Python의 컨테이너 유형과 정확히 동일합니다. +"hello world" ;=> "hello world" +; 문자열 연산은 Python과 유사하게 작동합니다. +(+ "hello " "world") ;=> "hello world" +; 목록은 []를 사용하여 생성되며, 인덱싱은 0부터 시작합니다. +(setv mylist [1 2 3 4]) +; 튜플은 불변 데이터 구조입니다. +(setv mytuple (, 1 2)) +; 사전은 키-값 쌍입니다. +(setv dict1 {"key1" 42 "key2" 21}) +; :name은 Hy에서 키워드를 정의하는 데 사용할 수 있으며, 키에 사용할 수 있습니다. +(setv dict2 {:key1 41 :key2 20}) +; `get`을 사용하여 인덱스/키에서 요소를 가져옵니다. +(get mylist 1) ;=> 2 +(get dict1 "key1") ;=> 42 +; 또는 키워드가 사용된 경우 직접 호출할 수 있습니다. +(:key1 dict2) ;=> 41 + +;; 함수 및 기타 프로그램 구성 +; 함수는 defn을 사용하여 정의되며, 마지막 sexp는 기본적으로 반환됩니다. +(defn greet [name] + "A simple greeting" ; 선택적 문서 문자열 + (print "hello " name)) + +(greet "bilbo") ;=> "hello bilbo" + +; 함수는 선택적 인수와 키워드 인수를 사용할 수 있습니다. +(defn foolists [arg1 &optional [arg2 2]] + [arg1 arg2]) + +(foolists 3) ;=> [3 2] +(foolists 10 3) ;=> [10 3] + +; 나머지 인수와 kwargs도 사용할 수 있습니다: +(defn something-fancy [wow &rest descriptions &kwargs props] + (print "Look at" wow) + (print "It's" descriptions) + (print "And it also has:" props)) + +(something-fancy "My horse" "amazing" :mane "spectacular") + +; 스플랫 연산자 대신 apply를 사용합니다: +(apply something-fancy ["My horse" "amazing"] { "mane" "spectacular" }) + +; 익명 함수는 `fn` 또는 `lambda` 구성을 사용하여 생성되며, `defn`과 유사합니다. +(map (fn [x] (* x x)) [1 2 3 4]) ;=> [1 4 9 16] + +;; 시퀀스 연산 +; Hy에는 시퀀스 연산 등을 위한 몇 가지 내장 유틸리티가 있습니다. +; `first` 또는 `car`를 사용하여 첫 번째 요소를 검색합니다. +(setv mylist [1 2 3 4]) +(setv mydict {"a" 1 "b" 2}) +(first mylist) ;=> 1 + +; cut을 사용하여 목록 슬라이스 +(cut mylist 1 3) ;=> [2 3] + +; `get`을 사용하여 목록 또는 사전에서 요소 가져오기 +(get mylist 1) ;=> 2 +(get mydict "b") ;=> 2 +; 목록 인덱싱은 Python과 마찬가지로 0부터 시작합니다. +; assoc은 키/인덱스에서 요소를 설정할 수 있습니다. +(assoc mylist 2 10) ; mylist를 [1 2 10 4]로 만듭니다. +(assoc mydict "c" 3) ; mydict를 {"a" 1 "b" 2 "c" 3}으로 만듭니다. +; 시퀀스 작업을 재미있게 만드는 다른 많은 핵심 함수가 있습니다. + +;; Python 상호 운용성 +;; import는 Python과 똑같이 작동합니다. +(import datetime) +(import functools [partial reduce]) ; functools에서 partial 및 reduce 가져오기 +(import matplotlib.pyplot :as plt) ; foo를 bar로 가져오기 +; 모든 내장 Python 메서드 등은 Hy에서 액세스할 수 있습니다. +; a.foo(arg)는 (.foo a arg)로 호출됩니다. +(.split (.strip "hello world ")) ;=> ["hello" "world"] + +; 값을 여러 함수에 실행하는 바로 가기인 "스레딩 매크로"가 있으며, 화살표로 표시됩니다: +(-> "hello world " (.strip) (.split)) ;=> ["hello" "world"] +; 화살표는 값을 첫 번째 인수로 호출을 따라 전달합니다. 예를 들어: +(-> 4 (* 3) (+ 2)) +; 다음과 같습니다: +(+ (* 4 3) 2) + +; "스레딩 꼬리 매크로"도 있으며, 대신 값을 두 번째 인수로 전달합니다. 비교: +(-> 4 (- 2) (+ 1)) ;=> 3 +(+ (- 4 2) 1) ;=> 3 +; ~에: +(->> 4 (- 2) (+ 1)) ;=> -1 +(+ 1 (- 2 4)) ;=> -1 + +;; 조건문 +; (if condition (body-if-true) (body-if-false) +(if (= passcode "moria") + (print "welcome") + (print "Speak friend, and Enter!")) + +; cond로 여러 if else if 절 중첩 +(cond + (= someval 42) (print "Life, universe and everything else!") + (> someval 42) (print "val too large") + (< someval 42) (print "val too small")) + +; do로 문 그룹화, 순차적으로 실행됩니다. +; defn과 같은 형식에는 암시적 do가 있습니다. +(do + (setv someval 10) + (print "someval is set to " someval)) ;=> 10 + +; `let`으로 어휘 바인딩 만들기, 이렇게 정의된 모든 변수는 로컬 범위를 가집니다. +(let [nemesis {"superman" "lex luther" + "sherlock" "moriarty" + "seinfeld" "newman"}] + (for [[h v] (.items nemesis)] + (print (.format "{0}'s nemesis was {1}" h v)))) + +;; 클래스 +; 클래스는 다음과 같이 정의됩니다. +(defclass Wizard [object] + (defn __init__ [self spell] + (setv self.spell spell)) + + (defn get-spell [self] + self.spell)) +``` + +### 추가 자료 + +이 튜토리얼은 Hy/Lisp/Python에 대한 기본적인 소개일 뿐입니다. + +Hy 문서는 여기에 있습니다: [https://hylang.org/hy/doc](https://hylang.org/hy/doc) + +Hy의 GitHub 리포지토리: [https://github.com/hylang/hy](https://github.com/hylang/hy) + +freenode IRC `#hy`, 트위터 해시태그 #hylang \ No newline at end of file diff --git a/ko/inform7.md b/ko/inform7.md new file mode 100644 index 0000000000..f880b65411 --- /dev/null +++ b/ko/inform7.md @@ -0,0 +1,193 @@ +--- +name: Inform7 +contributors: + - ["Hyphz", "http://github.com/hyphz/"] +filename: LearnInform.Inform +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Inform 7은 Graham Nelson과 Emily Short가 텍스트 어드벤처를 작성하기 위해 만든 자연어 기반 언어이지만, 특히 데이터 기반의 다른 텍스트 기반 애플리케이션에도 잠재적으로 사용할 수 있습니다. + +```inform7 +[이것은 주석입니다.] + +[Inform 7은 텍스트 어드벤처를 구축하기 위해 설계된 언어입니다. +다른 목적으로도 사용할 수 있지만, 기본 라이브러리는 텍스트 어드벤처를 구축합니다. Inform 7은 객체 지향입니다.] + +[이것은 하위 클래스화를 통해 클래스를 만듭니다. "Value"는 보편적인 하위 클래스이지만, "object"는 OO 객체처럼 작동하는 가장 기본적인 것입니다.] +A datablock is a kind of object. + +[클래스는 속성을 가질 수 있습니다.] +A datablock can be broken. [이것은 부울 속성을 만듭니다.] +A datablock is usually not broken. [이것은 기본값을 설정합니다.] +A datablock can be big or small. [이것은 열거형 속성을 만듭니다.] +A datablock is usually small. [이것은 기본값을 설정합니다.] +A datablock has a number called the sequence number. [이것은 유형이 지정된 속성을 만듭니다.] +A datablock has some text called the name. ["Some text"는 문자열을 의미합니다.] +A datablock has a datablock called the chain. [선언된 클래스는 유형이 됩니다.] + +[이것은 전역 명명된 인스턴스를 만듭니다.] +Block1 is a datablock. +The sequence number of Block1 is 1. +The name of Block1 is "Block One." + +[함수와 프로시저는 "구문"으로 정의됩니다.] +To do the thing everyone does with their first program: + say "Hello World.". [마침표는 끝을 나타내고, 들여쓰기는 범위를 나타냅니다.] + +To dump (the block - a datablock): [이것이 매개변수를 만드는 방법입니다.] + say the sequence number of the block; + say the name of the block; + if the block is broken, say "(Broken)". + +To toggle (the block - a datablock): + if the block is broken: [조건문.] + now the block is not broken; [속성 업데이트.] + else: + now the block is broken. + +[여러 매개변수.] +To fix (the broken block - a datablock) using (the repair block - a datablock): + if the broken block is not broken, stop; [들여쓰기 없는 단일 명령에 대한 쉼표.] + if the repair block is broken, stop; + now the sequence number of the broken block is the sequence number of the repair block; + now the broken block is not broken. + +[텍스트 어드벤처 기원 때문에 Inform 7은 일반적으로 객체를 동적으로 생성하는 것을 허용하지 않지만, 이를 가능하게 하는 언어 확장이 있습니다.] +Block2 is a datablock. +Block2 is broken. +The sequence number of Block2 is 2. +The name of Block2 is "Block two." + +To demonstrate calling a phrase with two parameters: + Let the second block be block2; [지역 포인터 변수.] + fix the second block using Block1; + say the sequence number of the second block. [1.] + +[목록.] +To show how to use list types: + let the list be a list of datablocks; + add Block1 to the list; + add Block2 to the list; + say the list; ["Block1 and Block2"] + [멤버십.] + if Block1 is listed in the list: + say "Block1 is there."; + [루프.] + repeat with the block running through the list: + dump the block; [1 Block One. 1 Block Two.] + [Remember block two's sequence number was changed above.] + let X be entry 2 of the list; [계산은 1부터 시작합니다.] + dump X; ["1 Block two."] + remove X from the list; + say the list. [Block1] + +[함수를 정의하고 산술을 수행하는 방법은 다음과 같습니다.] + +To decide which number is the sum of all numbers up to (X - a number) (this is summing up): + let the total so far be a number; + repeat with the current number running from 1 to X: + now the total so far is the total so far + the current number; + decide on the total so far. [이것은 반환 문입니다.] + +[ 고차 함수도 있습니다. ] + +To demonstrate a higher order function: + say summing up applied to {1, 2, 3, 4}. + +To decide which number is the result of applying (phrase - phrase A -> A) twice to (B - a value of kind A): + let b1 be phrase applied to B; + let b2 be phrase applied to b1; + decide on b2. + +To demonstrate defining a higher order function: + let X be 5; + say the result of applying summing up twice to X. + +[ 규칙집은 다른 조건에서 동일한 유형에 적용되는 여러 함수를 쌓을 수 있습니다. ] + +Datablock validation rules is a datablock based rulebook. + +A datablock validation rule for a broken datablock: rule fails. +A datablock validation rule for a datablock (called the block): + dump the block; + rule succeeds. + +To demonstrate invoking a rulebook: + follow datablock validation rules for Block1; + follow datablock validation rules for Block2. + +[ 객체는 관계형 데이터베이스의 관계와 유사한 관계를 가질 수도 있습니다. ] +A dog is a kind of thing. +Rover is a dog. +The kennel is a container. [이것은 내장된 기본 클래스입니다.] +Rover is in the kennel. [이것은 "포함"이라는 내장 관계를 만듭니다.] + +[유형을 선언하여 관계를 만들 수 있습니다.] + +Guide dog ownership relates one dog to one person. [일대일.] +Property ownership relates various things to one person. [다대일.] +Friendship relates various people to various people. [다대다.] + +[실제로 사용하려면 동사나 전치사를 할당해야 합니다.] + +The verb to own means the property ownership relation. +The verb to be the guide dog of means the guide dog ownership relation. +The verb to be guided by means the reversed guide dog ownership relation. +The verb to be friends with means the friendship relation. + +Edward is a person. A person can be blind. Edward is blind. +Edward is guided by Rover. +Benny is a person. Edward is friends with Benny. + +To demonstrate looking something up with a relation: + repeat with the dog running through things that are the guide dog of Edward: + say the dog; + repeat with the friend running through things that are friends with Edward: + say the friend. + +[절차적으로 존재하는 관계를 정의할 수도 있습니다.] + +Helpfulness relates a person (called the helper) to a person (called the helpee) when the helpee is blind and the helper is not blind. +The verb to be helpful to means the helpfulness relation. +To demonstrate using a procedural relation: + repeat with the helper running through people that are helpful to Edward: + say the helper. + + +[ 위 코드를 실행할 수 있도록 텍스트 어드벤처 하네스에 대한 인터페이스입니다. ] +Tutorial room is a room. +"A rather strange room full of buttons. Push them to run the exercises, or turn on the robot to run them all." +A button is a kind of thing. A button is fixed in place. + +The red button is a button in tutorial room. +Instead of pushing the red button, do the thing everyone does with their first program. +The green button is a button in tutorial room. +Instead of pushing the green button, demonstrate calling a phrase with two parameters. +The blue button is a button in tutorial room. +Instead of pushing the blue button, show how to use list types. +The cyan button is a button in tutorial room. +Instead of pushing the cyan button, say the sum of all numbers up to 5. +The purple button is a button in tutorial room. +Instead of pushing the purple button, demonstrate a higher order function. +The black button is a button in tutorial room. +Instead of pushing the black button, demonstrate defining a higher order function. +The white button is a button in tutorial room. +Instead of pushing the white button, demonstrate invoking a rulebook. +The puce button is a button in tutorial room. +Instead of pushing the puce button, demonstrate looking something up with a relation. +The orange button is a button in tutorial room. +Instead of pushing the orange button, demonstrate using a procedural relation. + +The robot is an object in tutorial room. +Instead of switching on the robot: + say "The robot begins to frantically flail its arms about."; + repeat with button running through buttons in the tutorial room: + say "The robot randomly hits [the button]."; + try pushing button. +``` + +## 더 배울 준비가 되셨습니까? + +* [Inform 7](http://www.inform7.com/) \ No newline at end of file diff --git a/ko/janet.md b/ko/janet.md new file mode 100644 index 0000000000..b0ca32fe95 --- /dev/null +++ b/ko/janet.md @@ -0,0 +1,309 @@ +--- +name: Janet +filename: learnJanet.janet +contributors: + - ["John Gabriele", "http://www.unexpected-vortices.com/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +[Janet](https://janet-lang.org/)은 Lisp와 유사한(Clojure와 유사한), 어휘적으로 범위가 지정되고, 동적으로 유형이 지정되며, 가비지 수집되고, C 기반의 고급 언어입니다. 전체 언어(핵심 라이브러리, 인터프리터, 컴파일러, 어셈블러, PEG)는 약 300-500kB이며 많은 제한된 시스템에서 실행되어야 합니다. + +아래 코드 조각을 Janet repl에서 시도해 보시기 바랍니다([Janet 설치](https://janet-lang.org/docs/index.html) 또는 Janet 홈페이지에 내장된 repl 사용). + +시간이 부족하므로 여기서는 기본 사항을 살펴보고 나머지 세부 정보는 설명서에 맡기겠습니다. 그러니 항상 차량 안에 팔과 다리를 두시고, 경치 좋은 여행을 계속합시다! + +```clojure +# 주석입니다. + +# 일부 리터럴 값입니다. +true +false +nil + +# 심볼(식별자/사물의 이름)에 대한 일반적인 스타일입니다. +do-stuff +pants-on-fire! +foo->bar # 분명히 foo를 bar로 변환하기 위한 것입니다. +fully-charged? +_ # 일반적으로 더미 변수로 사용됩니다. + +# 키워드는 콜론으로 시작하는 심볼과 같으며, 상수로 처리되고 일반적으로 맵 키 또는 매크로의 구문 조각으로 사용됩니다. +:a +:some-val + +# 숫자 ##################################################################### +5 +1e3 # => 1000 +1_000 # => 1000 +2e-03 # => 0.002 +0xff # => 255 + +# 다음과 같이 기수(밑)를 지정할 수 있습니다: +16rff # => 255 (0xff와 동일) +2r1101 # => 13 + +# 수학 라이브러리의 일부 숫자: +math/pi # => 3.14159 +math/e # => 2.71828 + +# 문자열 ##################################################################### +"hello" +"hey\tthere" # 탭 포함 + +# 여러 줄 문자열의 경우 하나 이상의 백틱을 사용하십시오. 이러한 문자열에서는 백슬래시 이스케이프가 인식되지 않습니다(바이트는 문자 그대로 구문 분석됨). +``a long +multi-line +string`` # => "a long\nmulti-line\nstring" + +# Janet의 문자열 및 데이터 구조는 변경 가능 및 불변의 두 가지 종류가 있습니다. 변경 가능한 종류의 리터럴은 앞에 `@`가 붙습니다. + +# 변경 가능한 문자열(일명 "버퍼"). +@"this" +@`a multi-line +one here` + +(string "con" "cat" "enate") # => "concatenate" + +# 하위 문자열을 얻으려면: +(string/slice "abcdefgh" 2 5) # => "cde" +# 하위 문자열을 찾으려면: +(string/find "de" "abcdefgh") # => 3 + +# 자세한 내용은 문자열 라이브러리를 참조하십시오(분할, 교체 등). + +# 데이터 구조 ############################################################# +# 배열 및 튜플 +# 배열은 변경 가능하고, 튜플은 불변입니다. + +# 배열 (변경 가능) +@(4 5 6) +@[4 5 6] + +# 튜플 (불변) +# 참고: 여는 괄호는 일반적으로 함수 호출을 나타내므로 괄호가 있는 리터럴 튜플을 원하면 "인용"해야 합니다(시작 작은따옴표 사용)... +'(4 5 6) +[4 5 6] # ... 또는 대괄호를 사용하십시오. + +# 테이블 및 구조체 (연관 데이터 구조) +@{:a 1 :b 2 :c 3} # 테이블 (변경 가능) +{:a 1 :b 2 :c 3} # 구조체 (불변) + +# 이것들을 "예쁘게 인쇄"하려면 `print` 대신 `pp`를 사용하십시오. +# 배열/튜플 및 테이블/구조체 작업 방법에 대한 자세한 내용은 아래를 참조하십시오. + +# 바인딩 #################################################################### +# 값을 심볼에 바인딩합니다. +(def x 4.7) # 상수 `x`를 정의합니다. +x # => 4.7 +(quote x) # => x (심볼 x) +'x # => x (심볼 x (약어)) +(print x) # 4.7 인쇄 + +# `def`를 사용했으므로 `x`가 참조하는 것을 변경할 수 없습니다: +(set x 5.6) # 오류, `x`는 상수입니다. + +(var y 10) +(set y 12) # `y`가 `var`를 사용하여 정의되었으므로 작동합니다. + +# 참고: 바인딩은 호출된 범위에 로컬입니다. `let`은 로컬 범위를 만들고 한 번에 일부 바인딩을 만듭니다: +(let [a 2 + b 3] + (print "Hello from inside this local scope.") + (* a b)) # => 6 + +# 구조 분해는 배열/튜플 모두에 대해 지원됩니다... +(def a ["foos" "bars" "moos"]) +(let [[s1 _ s2] a] + (print s1 s2)) # foosmoos + +# ... 및 테이블/구조체. +(def t {:a "ayy" :b "bee" :c "sea"}) +(let [{:a a :b b} t] + (print a b)) # ayybee + +# `def`에서 바로 구조 분해할 수도 있습니다: +(def [aa1 aa2] a) +aa1 # => foos +aa2 # => bars + +(def {:c body-of-water :b insect-friend} t) +body-of-water # => sea +insect-friend # => bee + +# 참고: 키워드는 자신으로 평가되는 반면, 심볼은 바인딩된 값으로 평가됩니다(인용하지 않는 한). + +# 연산자 ################################################################### +# Janet은 일반적인 연산자 앙상블을 지원합니다. +# +, -, *, / 등. 참고: +(/ 5 3) # => 1.66667 +(% 5 3) # => 2 (나머지) +(- 5) # => -5 (또는 `-5`라고 쓸 수 있습니다) + +(++ i) # 증가 ( `i` 수정) +(-- i) # 감소 +(+= i 3) # `i`에 3 추가 +(*= i 3) # `i`를 3배 +# ... 그리고 숫자에 대한 다른 연산도 마찬가지입니다. + +# `i`를 변경하고 싶지 않으면 `(inc i)` 및 `(dec i)`를 사용하십시오. + +# 비교 +# = < > not= <= >= +(< 2 7 12) # => true + +# 함수 ################################################################### +# 호출: +(- 5 3) # => 2 (연산자와 함수는 동일하게 작동합니다.) +(math/sin (/ math/pi 2)) # => 1 +(range 5) # => @[0 1 2 3 4] + +# 만들기: +(defn mult-by-2 + ``첫 번째 줄 문서 문자열. + + 문서 문자열의 일부 더.`` + [x] + (print "Hi.") + (print "Will compute using: " x) + (* 2 x)) + +(print (mult-by-2 6)) # => 12 ("Hi" 등을 인쇄한 후) + +# 파일에 "main"이라는 함수가 있으면 `janet`은 파일을 실행할 때 자동으로 호출합니다. + +# repl 내에서 함수의 문서를 대화식으로 읽습니다: +(doc mult-by-2) + +# 참고: 함수는 함수에서 사용되기 전에 정의되어야 하므로, 하향식으로 설계하는 경우 파일 맨 아래에서 위로 함수를 작성해야 합니다. + +# 익명 함수도 만들 수 있습니다: +(fn [x] (+ x x)) +(fn my-func [x] (+ x x)) # 이것은 덜 익명입니다. + +# `do`를 사용하여 일부 부작용 호출을 수행한 다음 `do`의 마지막 형식으로 평가합니다: +(def n (do + (print "hi") + (do-some-side-effecting 42) + 3)) +n # => 3 + +# 함수 본문은 "암시적 do"를 제공한다고 말할 수 있습니다. + +# 데이터 구조에 대한 연산 ############################################### +# (이 모든 것을 변경 가능하게 만들어 ... 변경할 수 있도록 합니다.) +(def s @"Hello, World!") +(def a @[:a :b :c :d :e]) +(def t @{:a 1 :b 2}) + +(length s) # => 13 +(length a) # => 5 +(length t) # => 2 + +# 값 가져오기: +(s 7) # => 87 ("W"의 코드 포인트) +(a 1) # => :b +(t :a) # => 1 +(keys t) # => @[:a :b] +(values t) # => @[1 2] + +# 값 변경 (변경 가능한 데이터 구조의 경우): +(put s 2 87) # @"HeWlo, World!" +(put a 2 :x) # @[:a :b :x :d :e] +(put t :b 42) # @{:a 1 :b 42} + +# 값 추가 및 제거 (다시, 변경 가능한 데이터 구조의 경우): +(buffer/push-string s "??") # @"HeWlo, World!?" +(array/push a :f) # @[:a :b :x :d :e :f] +(array/pop a) # => :f, 그리고 `a`에서도 제거됩니다. +(put t :x 88) # @{:a 1 :b 42 :x 88} + +# 버퍼/문자열, 배열/튜플 및 테이블/구조체 작업을 위한 다양한 함수에 대한 설명서를 참조하십시오. + +# 제어 흐름 ################################################################ +(if some-condition + 42 + 38) + +# `nil`과 `false`만 거짓입니다. 다른 모든 것은 참입니다. + +(if got-it? + 71) # 거짓 분기 값이 없습니다. `got-it?`이 거짓이면 `nil`을 반환합니다. + +(var i 10) +(while (pos? i) + (print "... " i) + (-- i)) +# 이제 `i`는 0입니다. + +# `case`는 디스패치 값을 각 옵션과 비교합니다. +(var x 2) +(case x + 1 "won" + 2 "too" + 3 "tree" + "unknown") # => "too" + +# `cond`는 `true`를 얻을 때까지 조건을 평가합니다. +(set x 8) +(cond + (= x 1) "won" + (= x 2) "too" + (< x 10) "tree" + "oof!") # => "tree" + +(when (avoided-wipeout?) + (do-side-effecty-thing 88) + (smell-the-roses) + (paint-fencepost-error)) + +# 패턴 매칭. +# `match`는 고성능 스위치 표현식과 같습니다. 데이터 구조를 전환하면 내부를 살펴보고 내용과 일치시키려고 할 수 있습니다. 예를 들어, 테이블 또는 구조체와 일치: +(def t {:a 1 :b 2 :c 3}) +(match t + {:yar v} (print "matches key :yar! " v) + {:moo v} (print "matches key :moo! " v) + {:c v} (print "matches key :c! " v) + _ (print "no match")) # => "matches key :c! 3" 인쇄 + +# 반복 ################################################################### +# 정수 범위 반복: +(for i 0 5 + (print i)) # 0, 1, 2, 3, 4 인쇄 + +# 더 일반적인 `loop`도 있습니다: +(loop [i :range [0 10] :when (even? i)] + (print i)) + +# 배열/튜플 반복: +(def words ["foo" "bar" "baz"]) +(each word words + (print word)) + +# 테이블/구조체 반복: +(def t {:a 1 :b 2}) +(eachp [k v] t # `t`의 각 쌍 반복. + (print k " --> " v)) + +# `eachk`를 사용하여 테이블 또는 구조체의 키를 반복할 수도 있습니다. + +# 함수형 프로그래밍 ###################################################### +# 여기에서 많은 익숙한 옛 친구를 찾을 수 있습니다. +(filter even? + (map (fn [x] + (* x x)) + (range 10))) # => @[0 4 16 36 64] + +(reduce + 0 (range 5)) # => 10 + +# ... 그리고 더 많습니다 (API 문서 참조). + +# 정오표 ###################################################################### +(type a) # `a`의 유형 (키워드로) +(describe a) # `a`에 대한 사람이 읽을 수 있는 설명 +(string/format "%j" a) # => Janet 값, 멋지게 서식 지정됨 +``` + +이 둘러보기는 모듈, 파이버, PEG, 매크로 등과 같은 다른 여러 기능을 다루지 않았지만, Janet이 어떤 것인지 맛볼 수 있을 것입니다. 자세한 내용은 [Janet 설명서](https://janet-lang.org/docs/index.html) 및 [Janet API 문서](https://janet-lang.org/api/index.html)를 참조하십시오. + +또한 Janet에 대한 심층적인 전자책인 [Janet for Mortals](https://janet.guide/)를 확인하십시오. \ No newline at end of file diff --git a/ko/jinja.md b/ko/jinja.md new file mode 100644 index 0000000000..f37340b6ff --- /dev/null +++ b/ko/jinja.md @@ -0,0 +1,261 @@ +--- +name: Jinja +contributors: + - ["Adaías Magdiel", "https://github.com/AdaiasMagdiel"] +filename: learn-jinja.j2 +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +## Jinja 시작하기 + +Jinja는 Python 애플리케이션을 위한 빠르고 표현력이 풍부하며 확장 가능한 템플릿 엔진입니다. + +Jinja에는 다음과 같은 많은 기능이 포함되어 있습니다: + +- 템플릿 상속 및 포함; +- 템플릿 내에서 매크로 정의 및 가져오기; +- XSS 공격을 방지하기 위한 보안 메커니즘; +- 신뢰할 수 없는 템플릿을 안전하게 렌더링할 수 있는 샌드박스 환경; +- 확장 가능한 필터, 테스트, 함수 및 구문까지. + +Jinja 템플릿은 단순히 텍스트 파일입니다. Jinja는 특정 확장자가 필요하지 않지만, 일부 IDE에서 더 쉽게 사용할 수 있도록 `.j2` 또는 `.jinja`를 사용하는 것이 일반적입니다. + +몇 가지 종류의 구분 기호가 있습니다. 기본 Jinja 구분 기호는 다음과 같이 구성됩니다: + +- `{% ... %}` 문 +- `{{ ... }}` 템플릿 출력에 인쇄할 표현식 +- `{# ... #}` 템플릿 출력에 포함되지 않는 주석 + +```jinja +{# 이것은 주석의 예입니다. #} + +{# + 이 구문을 사용하여 + 여러 줄 주석을 + 작성할 수도 있습니다. +#} +``` + + +## 변수 + +```jinja +{# 템플릿에 전달된 컨텍스트에서 변수에 액세스할 수 있습니다. #} + +{{ foo }} + +{# + 또한 점(.)을 사용하여 변수의 속성에 액세스하거나 + Python 구문을 사용하여 []를 사용할 수 있습니다. +#} + +{{ foo.bar }} +{{ foo['bar'] }} + +{# 템플릿 내에서 변수를 정의할 수도 있습니다. #} + +{% set name = "Magdiel" %} +{{ name }} +``` + +## 루프 + +```html +

Members

+
    +{% for user in users %} +
  • {{ user.username }}
  • +{% endfor %} +
+ + +
+{% for key, value in my_dict.items() %} +

{{ key }}

-

{{ value }}

+{% endfor %} +
+ + +
+{% for idx, url in enumerate(urls) %} + Go to url {{ idx + 1 }} +{% endfor %} +
+``` + +## 조건문 + +Jinja의 if 문은 Python의 if 문과 유사합니다. 가장 기본적인 형태에서는 변수가 정의되어 있고, 비어 있지 않으며, 거짓이 아닌지 확인하는 데 일반적으로 사용됩니다. + +```html +{% if users %} +
    +{% for user in users %} +
  • {{ user.username }}
  • +{% endfor %} +
+{% endif %} + + +{# 여러 분기의 경우 Python과 같이 elif 및 else를 사용할 수 있습니다. #} + + +{% if message.status == "error" %} +

{{ message.content }}

+{% elif message.status == "success" %} +

{{ message.content }}

+{% else %} +

{{ message.content }}

+{% endif %} +``` + +## 템플릿 상속 + +Jinja의 가장 강력한 기능 중 하나는 템플릿 상속입니다. 다른 파일에서 확장하고 자신의 내용으로 재정의할 수 있는 미리 정의된 블록이 있는 기본 레이아웃을 만들 수 있습니다. + +```html +{# file: base.html.j2 #} + + + + + {% block head %} + + + {% block title %}{% endblock title %} - Learning Jinja + {% endblock head %} + + +
+ {% block content %}{% endblock %} + {# endblock 태그에는 블록 이름이 필요하지 않습니다. #} +
+ + + + + +{# file: child.html.j2 #} + +{% extends "base.html.j2" %} + +{% block head %} + {{ super() }} + +{% endblock %} + +{% block title %}Home{% endblock %} + +{% block content %} +

Index

+

Welcome to my home homepage.

+{% endblock %} + + + +{# RESULT #} + + + + + + + Home - Learning Jinja + + + +
+

Index

+

Welcome to my home homepage.

+
+ + +``` + +### 내용 포함 + +`{% include "template/path" %}` 태그를 사용하여 현재 템플릿에 다른 템플릿의 내용을 포함할 수 있습니다. + +```html +{# file: footer.html.j2 #} + +
+

© 2024 - John Doe

+
+ + + +{# file: index.html.j2 #} +... + +
+

Hi! I'm John Doe!

+
+ {% include "footer.html.j2" %} + +... + + + +{# RESULT #} + +... + +
+

Hi! I'm John Doe!

+
+
+

© 2024 - John Doe

+
+ +... +``` + +기본 템플릿에 전달된 변수는 포함된 템플릿이 기본 템플릿의 컨텍스트에 액세스할 수 있으므로 포함된 템플릿에서도 사용할 수 있습니다. + +```html +{# file: greetings.html.j2 #} + +

I'm the {{ name }} and i like to {{ hobby }}.

+ + + +{# file: index.html.j2 #} + +{% set name = "Captain Nemo" %} +{% set hobby = "navigate through the depths of the ocean" %} + +
+ {% include "greetings.html.j2" %} +
+ + + +{# RESULT #} + +
+

I'm the Captain Nemo and i like to navigate through the depths of the ocean.

+
+``` + +## 매크로 + +매크로는 기본적으로 다른 언어의 함수와 같습니다. 인수가 있거나 없는 매크로를 정의하고 템플릿의 다양한 부분에서 재사용할 수 있습니다. + +```html +{% macro input(value="", type="text", placeholder="") -%} + +{%- endmacro %} + +

{{ input(placeholder="Your username") }}

+

{{ input(type="password") }}

+``` + +## 공식 문서 + +더 자세히 알아보려면 [공식 문서](https://jinja.palletsprojects.com/en/)에 액세스하십시오. \ No newline at end of file diff --git a/ko/jq.md b/ko/jq.md new file mode 100644 index 0000000000..ca9cb3be89 --- /dev/null +++ b/ko/jq.md @@ -0,0 +1,788 @@ +--- +category: tool +name: jq +contributors: + - ["Jack Kuan", "https://github.com/kjkuan"] + - ["Azeem Sajid", "https://github.com/iamazeem"] +filename: learnjq.sh +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +`jq`는 JSON 입력을 변환하고 JSON 출력을 생성하는 도구입니다. 프로그래밍 언어로서 jq는 부울 및 산술 표현식, 개체 및 배열 인덱싱을 지원합니다. 조건문, 함수 및 예외 처리까지 있습니다... 등. jq를 알면 JSON 문서에서 복잡한 쿼리를 수행하여 답을 찾거나, 보고서를 만들거나, 다른 프로그램에서 추가 처리를 위해 다른 JSON 문서를 생성하는 작은 프로그램을 쉽게 작성할 수 있습니다. + +> **참고**: 이 가이드는 명령줄에서 jq를 사용하는 방법을 보여주며, 특히 Bash 셸을 실행하는 환경에서 사용합니다. + +```bash +# 명령줄에서 jq를 실행할 때 jq 프로그램 코드는 `jq`에 대한 옵션 뒤에 첫 번째 인수로 지정할 수 있습니다. 우리는 종종 명령줄 셸에서 특별한 해석을 방지하기 위해 이러한 jq 프로그램을 작은따옴표(`'`)로 묶습니다. +# +jq -n '# 주석은 #으로 시작하여 줄 끝까지 이어집니다. + # -n 옵션은 입력을 `null` 값으로 설정하고 `jq`가 외부 소스에서 입력을 읽는 것을 방지합니다. +' + +# 출력: +# null + + +# 기본적으로 jq는 *STDIN*에서 JSON 입력(값) 스트림을 읽습니다. 명령줄에 지정된 jq 프로그램(필터)으로 각 입력을 처리하고, 프로그램을 사용하여 각 입력을 처리한 출력을 *STDOUT*으로 인쇄합니다. +# +echo ' + "hello" 123 [ + "one", + "two", + "three" + ] + { "name": "jq" } +' | + jq '. # <-- 여기서 jq 프로그램은 단일 점(.)이며, 현재 입력을 나타내는 ID 연산자라고 합니다. +' + +# 출력: +# "hello" +# 123 +# [ +# "one", +# "two", +# "three" +# ] +# { +# "name": "jq" +# } + + +# jq는 기본적으로 출력을 예쁘게 인쇄하므로, `jq`로 파이핑하는 것은 JSON을 반환하는 일부 REST API 엔드포인트의 응답을 서식 지정하는 간단한 방법입니다. 예: `curl -s https://freegeoip.app/json/ | jq` + + +# 각 JSON 입력을 jq 프로그램으로 처리하는 대신, jq에 배열로 한 번에 읽도록 요청할 수도 있습니다. +# +echo '1 "two" 3' | jq -s . + +# 출력: +# [ +# 1, +# "two", +# 3 +# ] + + +# 또는 각 줄을 문자열로 처리합니다. +# +(echo line 1; echo line 2) | jq -R . + +# 출력: +# "line 1" +# "line 2" + + +# 또는 -s와 -R을 결합하여 입력 줄을 단일 문자열로 한 번에 읽습니다. +# +(echo line 1; echo line 2) | jq -sR . + +# 출력: +# "line 1\nline2\n" + + +# 입력은 명령줄에 지정된 JSON 파일에서도 가져올 수 있습니다: +# +echo '"hello"' > hello.json +jq . hello.json + +# 출력: +# "hello" + + +# `--arg` 옵션을 사용하여 jq 프로그램에 값을 전달할 수 있습니다. 아래에서 `val`은 값 `123`을 바인딩할 변수 이름입니다. 그런 다음 변수는 `$val`로 참조됩니다. +# +jq -n --arg val 123 '$val' # $val은 여기서 문자열 "123"입니다. + +# 출력: +# "123" + + +# JSON 값을 전달해야 하는 경우 `--argjson`을 사용하십시오. +# +jq -n --argjson val 123 '$val' # $val은 숫자입니다. + +# 출력: +# 123 + + +# `--arg` 또는 `--argjson`을 사용하는 것은 기존 입력에서 JSON 출력을 빌드하는 데 유용한 방법입니다. +# +jq --arg text "$(date; echo "Have a nice day!")" -n '{ "today": $text }' + +# 출력: +# { +# "today": "Sun Apr 10 09:53:07 PM EDT 2022\nHave a nice day!" +# } + + +# 값을 JSON으로 출력하는 대신 `-r` 옵션을 사용하여 문자열 값을 따옴표 없이/이스케이프 없이 인쇄할 수 있습니다. 문자열이 아닌 값은 여전히 JSON으로 인쇄됩니다. +# +echo '"hello" 2 [1, "two", null] {}' | jq -r . + +# 출력: +# hello +# 2 +# [ +# 1, +# "two", +# null +# ] +# {} + + +# jq의 문자열 내에서 `\(expr)`을 사용하여 `expr`의 출력을 주변 문자열 컨텍스트에 대체할 수 있습니다. +# +jq -rn '"1 + 2 = \(1+2)"' + +# 출력: +# 1 + 2 = 3 + + +# `-r` 옵션은 셸 파이프라인에서 아래로 처리될 텍스트 출력을 생성하는 데 가장 유용하며, 특히 `@sh` 접두사 연산자가 접두사로 붙은 보간된 문자열과 결합될 때 유용합니다. +# +# `@sh` 연산자는 문자열 내의 `\( ... )` 출력을 작은따옴표로 이스케이프하여 `\( ... )`의 각 결과 문자열이 특별한 해석 없이 셸에서 단일 단어/토큰/인수로 평가될 수 있도록 합니다. +# +env_vars=( + echo '{"var1": "value one", "var2": "value\ntwo"}' \ + | + jq -r ' + "export " + @sh "var1=\(.var1) var2=\(.var2)" + # ^^^^^^^^ ^^^^^^^^ + # "'value one'" "'value\ntwo'" + # + # 참고: 여기서 + (더하기) 연산자는 문자열을 연결합니다. + ' +) +echo "$env_vars" +eval "$env_vars" +declare -p var1 var2 + +# 출력: +# export var1='value one' var2='value +two' +# declare -- var1="value one" +# declare -- var2="value +two" + +# `@base64`, `@uri`, `@csv` 등과 같이 유용할 수 있는 다른 문자열 `@prefix` 연산자가 있습니다. 자세한 내용은 `man jq`를 참조하십시오. + + +# jq의 쉼표(`,`) 연산자는 각 피연산자를 평가하고 여러 출력을 생성합니다: +# +jq -n '"one", 2, ["three"], {"four": 4}' + +# 출력: +# "one" +# 2 +# [ +# "three" +# ] +# { +# "four": 4 +# } + + +# 모든 JSON 값은 JSON 값 자체로 평가되는 유효한 jq 표현식입니다. +# +jq -n '1, "one", [1, 2], {"one": 1}, null, true, false' + +# 출력: +# 1 +# "one" +# [ +# 1, +# 2 +# ] +# { +# "one": 1 +# } +# null +# true +# false + + +# 모든 jq 표현식은 JSON 값이 예상되는 모든 곳에서 사용할 수 있으며, 개체 키로도 사용할 수 있습니다. (개체 키 또는 값에 괄호가 필요할 수 있음) +# +jq -n '[2*3, 8-1, 16/2], {("tw" + "o"): (1 + 1)}' + +# 출력: +# [ +# 6, +# 7, +# 8 +# ] +# { +# "two": 2 +# } + + +# 바로 가기로, JSON 개체 키가 유효한 식별자(정규식 `^[a-zA-Z_][a-zA-Z_0-9]*$`와 일치)처럼 보이면 따옴표를 생략할 수 있습니다. +# +jq -n '{ key_1: "value1" }' + +# JSON 개체의 키 값이 생략되면 현재 입력에서 키를 사용하여 조회됩니다: (다음 예제에서 `... | ...`의 의미 참조) +# +jq -n '{c: 3} | {a: 1, "b", c}' + +# 출력: +# { +# "a": 1, +# "b": null, +# "c": 3 +# } + + +# jq 프로그램은 일반적으로 파이프(`|`) 연산자로 연결된 일련의 표현식(필터)으로 작성되며, 왼쪽 필터의 출력을 오른쪽 필터의 입력으로 만듭니다. +# +jq -n '1 | . + 2 | . + 3' # 첫 번째 점은 1이고, 두 번째 점은 3입니다. + +# 출력: +# 6 + +# 표현식이 여러 출력으로 평가되면 jq는 이를 반복하고 각 출력을 파이프라인 아래로 전파하여 결국 여러 출력을 생성합니다. +# +jq -n '1, 2, 3 | ., 4 | .' + +# 출력: +# 1 +# 4 +# 2 +# 4 +# 3 +# 4 + +# 마지막 예제의 데이터 흐름은 다음과 같이 시각화할 수 있습니다: +# (`*` 접두사가 붙은 숫자는 현재 출력을 나타냅니다.) +# +# *1, 2, 3 | *1, 4 | *1 +# 1, 2, 3 | 1, *4 | *4 +# 1, *2, 3 | *2, 4 | *2 +# 1, 2, 3 | 2, *4 | *4 +# 1, 2, *3 | *3, 4 | *3 +# 1, 2, 3 | 3, *4 | *4 +# +# +# 다른 말로 하면, 위 예제의 평가는 다른 프로그래밍 언어의 다음 코드 조각과 매우 유사합니다: +# +# Python: +# +# for first_dot in 1, 2, 3: +# for second_dot in first_dot, 4: +# print(second_dot) +# +# Ruby: +# +# [1, 2, 3].each do |dot| +# [dot, 4].each { |dot| puts dot } +# end +# +# JavaScript: +# +# [1, 2, 3].forEach(dot => { +# [dot, 4].forEach(dot => console.log(dot)) +# }) +# + + +# 다음은 표현식 뒤에 `[expr]` 연산자를 사용하는 배열 인덱스 및 개체 속성 조회 예제입니다. `expr`이 숫자이면 배열 인덱스 조회이고, 그렇지 않으면 문자열이어야 하며, 이 경우 개체 속성 조회입니다: + +# 배열 인덱스 조회 +# +jq -n '[2, {"four": 4}, 6][1 - 1]' # => 2 +jq -n '[2, {"four": 4}, 6][0]' # => 2 +jq -n '[2, {"four": 4}, 6] | .[0]' # => 2 + +# 조회를 연결할 수 있습니다. 표현식일 뿐이기 때문입니다. +# +jq -n '[2, {"four": 4}, 6][1]["fo" + "ur"]' # => 4 + +# 개체 속성의 경우 `.key` 바로 가기를 사용할 수도 있습니다. +# +jq -n '[2, {"four": 4}, 6][1].four' # => 4 + +# 키가 유효한 식별자가 아닌 경우 `."key"`를 사용하십시오. +# +jq -n '[2, {"f o u r": 4}, 6][1]."f o u r"' # => 4 + +# 배열 인덱스 조회는 인덱스를 찾을 수 없는 경우 null을 반환합니다. +# +jq -n '[2, {"four": 4}, 6][99]' # => null + +# 개체 속성 조회는 키를 찾을 수 없는 경우 null을 반환합니다. +# +jq -n '[2, {"four": 4}, 6][1].whatever' # => null + +# 대체 연산자 `//`는 왼쪽 피연산자의 결과가 `null` 또는 `false`일 때 기본값을 제공하는 데 사용할 수 있습니다. +# +jq -n '.unknown_key // 7' # => 7 + +# 조회 연산자(`[expr]`) 앞의 것이 배열이나 개체가 아닌 경우 오류가 발생합니다: +# +jq -n '123 | .[0]' # => jq: error (at ): Cannot index number with number +jq -n '"abc" | .name' # => jq: error (at ): Cannot index string with string "name" +jq -n '{"a": 97} | .[0]' # => jq: error (at ): Cannot index object with number +jq -n '[89, 64] | .["key"]' # => jq: error (at ): Cannot index array with string "key" + +# 그러나 조회에 `?`를 추가하여 jq가 이러한 오류가 발생할 때 `empty`를 반환하도록 할 수 있습니다. +# +jq -n '123 | .[0]?' # 출력이 없으므로 비어 있습니다. +jq -n '"abc" | .name?' # 출력이 없으므로 비어 있습니다. + +# 대체 연산자(`//`)는 `empty`에서도 작동합니다: +# +jq -n '123 | .[0]? // 99' # => 99 +jq -n '"abc" | .name? // "unknown"' # => "unknown" + +# 참고: `empty`는 실제로 jq의 내장 함수입니다. +# 이전에 설명한 중첩 루프 설명에서 `empty`는 일부 프로그래밍 언어에서 루프의 현재 반복을 건너뛰는 `continue` 또는 `next` 키워드와 같습니다. + + +# 문자열과 배열은 Python 프로그래밍 언어에서와 동일한 구문(`[i:j]`, 단 단계 없음) 및 의미 체계로 슬라이스할 수 있습니다: +# +# 0 1 2 3 4 5 ... 무한 +# array = ["a", "b", "c", "d"] +# -무한 ... -4 -3 -2 -1 +# +jq -n '["Peter", "Jerry"][1]' # => "Jerry" +jq -n '["Peter", "Jerry"][-1]' # => "Jerry" +jq -n '["Peter", "Jerry", "Tom"][1:]' # => ["Jerry", "Tom"] +jq -n '["Peter", "Jerry", "Tom"][:1+1]' # => ["Peter", "Jerry"] +jq -n '["Peter", "Jerry", "Tom"][1:99]' # => ["Jerry", "Tom"] + + +# 조회 인덱스 또는 키가 생략되면 jq는 컬렉션을 반복하여 각 반복에서 하나의 출력 값을 생성합니다. +# +# 이러한 예제는 동일한 출력을 생성합니다. +# +echo 1 2 3 | jq . +jq -n '1, 2, 3' +jq -n '[1, 2, 3][]' +jq -n '{a: 1, b: 2, c: 3}[]' + +# 출력: +# 1 +# 2 +# 3 + + +# 여러 출력으로 배열을 만들 수 있습니다. +# +jq -n '{values: [{a: 1, b: 2, c: 3}[] | . * 2]}' + +# 출력: +# { +# "values": [ +# 2, +# 4, +# 6 +# ] +# } + + +# 여러 출력이 포함되지 않은 경우 결국 여러 출력을 얻게 됩니다. +# +jq -n '{values: ({a: 1, b: 2, c: 3}[] | . * 2)}' + +# 출력: +# { +# "values": 2 +# } +# { +# "values": 4 +# } +# { +# "values": 6 +# } + + +# jq의 조건부 `if ... then ... else ... end`는 표현식이므로 `then` 부분과 `else` 부분이 모두 필요합니다. jq에서는 `null`과 `false`라는 두 값만 거짓이고 다른 모든 값은 참입니다. +# +jq -n 'if 1 > 2 | not and 1 <= 2 then "Makes sense" else "WAT?!" end' + +# 출력 +# "Makes sense" + +# `not`은 0개의 인수를 사용하는 내장 함수이므로 입력 값을 부정하는 필터로 사용됩니다. 함수에 대해서는 곧 이야기하겠습니다. + +# 조건부를 사용하는 또 다른 예: +# +jq -n '1, 2, 3, 4, 5 | if . % 2 != 0 then . else empty end' + +# 출력 +# 1 +# 3 +# 5 + + +# 위의 `empty`는 0개의 인수를 사용하고 출력을 생성하지 않는 내장 함수입니다. 내장 함수의 더 많은 예를 살펴보겠습니다. + +# 위 조건부 예제는 `select/1` 내장 함수(`/1`은 함수가 예상하는 인수 수를 나타냄)를 사용하여 작성할 수 있습니다. +# +jq -n '1, 2, 3, 4, 5 | select(. % 2 != 0)' # 참고: %는 나머지를 제공합니다. + +# 출력 +# 1 +# 3 +# 5 + + +# jq의 함수 인수는 이름으로 호출 의미 체계로 전달됩니다. 즉, 인수는 호출 사이트에서 평가되지 않고 대신 호출 사이트의 호출 컨텍스트를 변수 및 함수 참조에 대한 범위로 사용하는 람다 표현식으로 처리됩니다. +# +# 위 예제에서 `. % 2 != 0` 표현식은 함수에 전달되기 전에 (부울) 표현식이 평가되었을 경우 `true` 또는 `false`가 되었을 것과 달리 `select/1`에 인수로 전달되는 것입니다. + + +# `range/1`, `range/2` 및 `range/3` 내장 함수는 지정된 범위 내의 정수를 생성합니다. +# +jq -n '[range(3)]' # => [0, 1, 2] +jq -n '[range(0; 4)]' # => [0, 1, 2, 3] +jq -n '[range(2; 10; 2)]' # => [2, 4, 6, 8] + +# 함수 인수를 구분하는 데 `;`(세미콜론)가 사용됩니다. + + +# `map/1` 함수는 현재 입력(배열)의 각 요소에 지정된 표현식을 적용하고 새 배열을 출력합니다. +# +jq -n '[range(1; 6) | select(. % 2 != 0)] | map(. * 2)' + +# 출력: +# [ +# 2, +# 6, +# 10 +# ] + +# `select/1` 및 `map/1`을 사용하지 않고 위 예제를 다음과 같이 작성할 수도 있습니다: +# +jq -n '[range(1; 6) | if . % 2 != 0 then . else empty end | . * 2]' + + +# `keys/0`은 현재 입력의 키 배열을 반환합니다. 개체의 경우 개체의 속성 이름이고, 배열의 경우 배열 인덱스입니다. +# +jq -n '[range(2; 10; 2)] | keys' # => [0, 1, 2, 3] +jq -n '{a: 1, b: 2, c: 3} | keys' # => ["a", "b", "c"] + +# `values/0`은 현재 입력의 값 배열을 반환합니다. 개체의 경우 개체의 속성 값이고, 배열의 경우 배열의 요소입니다. +# +jq -n '[range(2; 10; 2)] | values' # => [2, 4, 6, 8] +jq -n '{a: 1, b: 2, c: 3} | values' # => [1, 2, 3] + + +# `to_entries/0`은 현재 입력 개체의 키-값 개체 배열을 반환합니다. +# +jq -n '{a: 1, b: 2, c: 3} | to_entries' + +# 출력: +# [ +# { +# "key": "a", +# "value": 1 +# }, +# { +# "key": "b", +# "value": 2 +# }, +# { +# "key": "c", +# "value": 3 +# } +# ] + + +# 다음은 지금까지 배운 내용을 사용하여 개체의 속성을 환경 변수로 바꾸는 방법입니다. +# +env_vars=( + jq -rn '{"var1": "1 2 3 4", "var2": "line1\nline2\n"} + | to_entries[] + | "export " + @sh "\(.key)=\(.value)" + ' +) +eval "$env_vars" +declare -p var1 var2 + +# 출력: +# declare -x var1="1 2 3 4" +# declare -x var2="line1 +# line2 +# " + + +# `from_entries/0`은 키-값 개체 배열을 가져와 개체의 `key` 및 `value` 속성에서 키와 값이 있는 개체로 바꾸는 `to_entries/0`의 반대입니다. +# +# 개체의 각 속성을 반복하고 무언가를 수행해야 할 때 `to_entries/0`과 함께 사용하는 것이 유용합니다. +# +jq -n '{a: 1, b: 2, c: 3} | to_entries | map(.value *= 2) | from_entries' + +# 출력: +# { +# "a": 2, +# "b": 4, +# "c": 6 +# } + + +# 위 예제는 `with_entries/1` 내장 기능으로 더 단축할 수 있습니다: +# +jq -n '{a: 1, b: 2, c: 3} | with_entries(.value *= 2)' + + +# `group_by/1`은 현재 입력(배열)에서 그룹(배열) 배열을 생성합니다. 분류는 입력 배열의 각 멤버에 표현식 인수를 적용하여 수행됩니다. +# +# 다음은 인위적인 예제입니다(참고: `tostring`, `tonumber`, `length` 및 `max`는 모두 내장 jq 함수입니다. jq 설명서에서 자유롭게 찾아보십시오): +# +# 일부 난수 생성. +numbers=$(echo $RANDOM{,,,,,,,,,,,,,,,,,,,,}) +# +# 숫자를 jq에 공급하고, 그룹으로 분류하고, 평균을 계산하고, 마지막으로 보고서를 생성합니다. +# +echo $numbers | jq -rs ' # 숫자를 배열로 한 번에 읽습니다. +# [ +# [ +# map(tostring) # 문자열 배열로 변환합니다. +# | group_by(.[0:1]) # 첫 번째 숫자로 숫자를 그룹화합니다. +# | .[] # 배열의 배열(그룹)을 반복합니다. +# | map(tonumber) # 각 그룹을 다시 숫자 배열로 변환합니다. +# ] # 마지막으로 모든 그룹을 배열에 포함합니다. + +# | sort_by([length, max]) # 그룹을 크기별로 정렬합니다. +# # 두 그룹의 크기가 같으면 가장 큰 숫자가 있는 그룹이 이깁니다(더 큽니다). + +# | to_entries[] # 배열을 열거하여 키-값 개체를 생성합니다. +# | # 각 개체에 대해 두 줄을 생성합니다: +# "Group \(.key): \(.value | sort | join(" "))" + "\n" + +# "Average: \( .value | (add / length) )" + +# ] # 그룹+평균 줄을 배열에 포함합니다. +# # 배열 요소를 구분선(대시)으로 결합하여 보고서를 생성합니다. +# | join("\n" + "-"*78 + "\n") +# ' + +# 출력: +# +# Group 0: 3267 +# Average: 3267 +# ------------------------------------------------------------------------------ +# Group 1: 7854 +# Average: 7854 +# ------------------------------------------------------------------------------ +# Group 2: 4415 4447 +# Average: 4431 +# ------------------------------------------------------------------------------ +# Group 3: 681 6426 +# Average: 3553.5 +# ------------------------------------------------------------------------------ +# Group 4: 21263 21361 21801 21832 22947 23523 29174 +# Average: 23128.714285714286 +# ------------------------------------------------------------------------------ +# Group 5: 10373 12698 13132 13924 17444 17963 18934 18979 +# Average: 15430.875 + + +# `add/1` 내장 기능은 값 배열을 단일 값으로 "축소"합니다. 컬렉션의 각 값 사이에 `+` 연산자를 붙이는 것으로 생각할 수 있습니다. 다음은 몇 가지 예입니다: +# +jq -n '[1, 2, 3, 4, 5] | add' # => 15 +jq -n '["a", "b", "c"] | add' # => "abc" + +# `+`는 배열을 연결합니다. +jq -n '[["a"], ["b"], ["c"]] | add' + +# 출력: +# [ +# "a", +# "b", +# "c" +# ] + +# `+`는 개체를 재귀적으로 병합하지 않습니다. +jq -n '[{"a": 1, "b": {"c": 3}}, {"b": 2, "c": 4}] | add' + +# 출력: +# { +# "a": 1, +# "b": 2, +# "c": 4 +# } + + +# jq는 지정된 표현식에서 생성된 출력을 단일 값으로 축소하는 표현식을 작성하기 위한 특수 구문을 제공합니다. 이 형식은 다음과 같습니다: +# +# reduce outputs_expr as $var (initial_value; reduction_expr) +# +# 예: +# +jq -n 'reduce range(1; 6) as $i (0; . + $i)' # => 15 +jq -n 'reduce (1, 2, 3, 4, 5) as $i (0; . + $i)' # => 15 +jq -n '[1, 2, 3, 4, 5] | reduce .[] as $i (0; . + $i)' # => 15 +jq -n '["a", "b", "c"] | reduce .[] as $i ("", . + $i)' # => "abc" + +# `reduction_expr`의 `.`는 처음에는 `initial_value`이고, `outputs_expr`의 값을 반복하면서 `reduction_expr`을 적용한 결과가 됩니다. 표현식: +# +# reduce (1, 2, 3, 4, 5) as $i (0; . + $i) +# +# 다음과 같이 생각할 수 있습니다: +# +# 0 + 1 | . + 2 | . + 3 | . + 4 | . + 5 + + +# `*` 연산자는 두 개체에 사용될 때 둘 다 재귀적으로 병합합니다. 따라서 JSON 개체를 재귀적으로 병합하려면 `reduce`와 `*` 연산자를 사용할 수 있습니다. 예를 들어: +# +echo ' + {"a": 1, "b": {"c": 3}} + { "b": {"d": 4}} + {"a": 99, "e": 5 } +' | jq -s 'reduce .[] as $m ({}; . * $m)' + +# 출력: +# { +# "a": 99, +# "b": { +# "c": 3, +# "d": 4 +# }, +# "e": 5 +# } + + +# jq에는 `expr as $var` 형식의 변수 할당이 있으며, `expr`의 값을 `$var`에 바인딩하고 `$var`는 불변입니다. 또한 `... as ...`는 다음 필터의 입력을 변경하지 않습니다. 필터 파이프라인에 도입되는 것은 값을 변수에 바인딩하기 위한 것이며, 해당 범위는 정의를 따르는 필터로 확장됩니다. (즉, 변수의 정의를 찾으려면 사용하는 표현식에서 필터 체인의 왼쪽으로 스캔하여 정의를 찾을 때까지) +# +jq -rn '[1, 2, 3, 4, 5] + | (.[0] + .[-1]) as $sum # 놀라움을 피하기 위해 항상 바인딩 `expr` 주위에 ( )를 넣으십시오. + | ($sum * length / 2) as $result # 이 단계의 현재 입력은 여전히 초기 배열입니다. + | "The result is: \($result)" # 동일. +' + +# 출력: +# The result is: 15 + + +# `expr as $var` 형식을 사용하면 `expr`에서 여러 값이 생성되는 경우 jq는 이를 반복하고 각 값을 차례로 `$var`에 바인딩하여 나머지 파이프라인을 실행합니다. +# +jq -rn 'range(2; 4) as $i + | range(1; 6) as $j + | "\($i) * \($j) = \($i * $j)"' + +# 출력: +# 2 * 1 = 2 +# 2 * 2 = 4 +# 2 * 3 = 6 +# 2 * 4 = 8 +# 2 * 5 = 10 +# 3 * 1 = 3 +# 3 * 2 = 6 +# 3 * 3 = 9 +# 3 * 4 = 12 +# 3 * 5 = 15 + + +# 프로그램 시작 시 초기 입력을 변수에 바인딩하여 나중에 파이프라인 아래에서 참조할 수 있도록 하는 것이 때때로 유용합니다. +# +jq -rn "$(cat <<'EOF' + {lookup: {a: 1, b: 2, c: 3}, + bonuses: {a: 5, b: 2, c: 9} + } + | . as $doc + | .bonuses + | to_entries[] + | "\(.key)'s total is \($doc.lookup[.key] + .value)" +EOF +)" + +# 출력: +# a's total is 6 +# b's total is 4 +# c's total is 12 + + +# jq는 변수 바인딩 중에 구조 분해를 지원합니다. 이를 통해 배열 또는 개체에서 값을 추출하고 변수에 바인딩할 수 있습니다. +# +jq -n '[range(5)] | . as [$first, $second] | $second' + +# 출력: +# 1 + +jq -n '{ name: "Tom", numbers: [1, 2, 3], age: 32} + | . as { + name: $who, # .name을 $who에 바인딩 + $name, # `name: $name`의 약어 + numbers: [$first, $second], + } + | $name, $second, $first, $who +' + +# 출력: +# "Tom" +# 2 +# 1 +# "Tom" + + +# jq에서 값은 할당 연산자 `=`를 통해 배열 인덱스 또는 개체 키에 할당될 수 있습니다. 동일한 현재 입력이 할당 연산자의 양쪽에 제공되며, 할당 자체는 현재 입력으로 평가됩니다. 즉, 할당 표현식은 부작용으로 평가되며 새 출력을 생성하지 않습니다. +# +jq -n '.a = 1 | .b = .a + 1' # => {"a": 1, "b": 2} + +# 참고: `jq -n`으로 인해 입력이 `null`이므로 첫 번째 필터에서 `.`는 `null`이며, `null` 아래의 키에 할당하면 키가 있는 개체로 바뀝니다. 그런 다음 동일한 입력(이제 개체)이 다음 필터로 파이핑되어 `b` 키를 `a` 키 값에 `1`을 더한 값인 `2`로 설정합니다. +# + +# 또 다른 예: +# +jq -n '.a=1, .a.b=2' # => {"a": 1} {"a": {"b": 2}} + +# 위 예제에서 두 개체는 두 할당 모두 입력으로 `null`을 받았고 쉼표 연산자의 각 피연산자가 독립적으로 평가되기 때문에 생성됩니다. 중첩된 개체를 쉽게 생성할 수 있는 방법도 확인하십시오. + + +# 할당 연산자 외에도 jq에는 `+=`, `-=`, `*=`, `/=` 등과 같은 연산자도 있습니다. 기본적으로 `a op= b`는 `a = a op b`의 약어이며, 현재 값을 기반으로 개체 속성 또는 배열의 항목을 업데이트하는 데 편리합니다. 예: +# +jq -n '.a.b.c = 3 | .a.b.c = .a.b.c + 1' # => {"a": {"b": {"c": 4}}} +jq -n '.a.b.c = 3 | .a.b.c += 1' # => {"a": {"b": {"c": 4}}} + + +# 값을 삭제하려면 삭제할 항목의 위치를 지정하는 경로 표현식을 사용하는 `del/1`을 사용하십시오. 예: +# +jq -n '{a: 1, b: {c: 2}, d: [3, 4, 5]} | del(.b.c, .d[1]) | .b.x = 6' + +# 출력: +# { +# "a": 1, +# "b": { +# "x": 6 +# }, +# "d": [ +# 3, +# 5 +# ] +# } + + +# jq의 내장 함수를 사용하는 것 외에도 자신만의 함수를 정의할 수 있습니다. 실제로 많은 내장 함수는 jq를 사용하여 정의됩니다(문서 끝에 있는 jq의 내장 함수 링크 참조). +# +jq -n ' + def my_select(expr): if expr then . else empty end; + def my_map(expr): [.[] | expr]; + def sum: reduce .[] as $x (0; . + $x); + def my_range($from; $to): + if $from >= $to then + empty + else + $from, my_range($from + 1; $to) + end + ; + [my_range(1; 6)] | my_map(my_select(. % 2 != 0)) | sum +' + +# 출력: +# 9 + +# 함수 정의에 대한 몇 가지 참고 사항: +# +# - 함수는 일반적으로 jq 프로그램의 나머지 부분에서 사용할 수 있도록 처음에 정의됩니다. +# +# - 각 함수 정의는 `;`(세미콜론)로 끝나야 합니다. +# +# - 다른 함수 내에서 함수를 정의할 수도 있지만 여기서는 보여주지 않습니다. +# +# - 함수 매개변수는 `;`(세미콜론)로 구분됩니다. 이것은 함수를 호출할 때 여러 인수를 전달하는 것과 일치합니다. +# +# - 함수는 자신을 호출할 수 있습니다. 실제로 jq에는 TCO(꼬리 호출 최적화)가 있습니다. +# +# - `def f($a; $b): ...;`는 `def f(a; b): a as $a | b as $b | ...`의 약어입니다. \ No newline at end of file diff --git a/ko/jquery.md b/ko/jquery.md new file mode 100644 index 0000000000..abef887430 --- /dev/null +++ b/ko/jquery.md @@ -0,0 +1,129 @@ +--- +category: framework +name: jQuery +contributors: + - ["Sawyer Charles", "https://github.com/xssc"] + - ["Devansh Patil", "https://github.com/subtra3t"] +filename: jquery.js +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +jQuery는 "더 적게 쓰고, 더 많이 하라"는 데 도움이 되는 JavaScript 라이브러리입니다. 많은 일반적인 JavaScript 작업을 더 쉽게 작성할 수 있도록 합니다. jQuery는 많은 대기업과 개발자가 어디에서나 사용합니다. AJAX, 이벤트 처리, 문서 조작 등을 더 쉽고 빠르게 만듭니다. + +jQuery는 JavaScript 라이브러리이므로 [먼저 JavaScript를 배워야 합니다](../javascript/) + +**참고**: 최근 몇 년 동안 jQuery는 바닐라 DOM(문서 개체 모델) API로 동일한 작업을 수행할 수 있기 때문에 주목을 받지 못했습니다. 따라서 사용되는 유일한 것은 [jQuery 날짜 선택기](https://api.jqueryui.com/datepicker)(실제로 `` HTML 요소와 달리 표준이 있음)와 같은 몇 가지 편리한 기능과 코드 길이의 명백한 감소입니다. + +```js +/////////////////////////////////// +// 1. 선택자 + +// jQuery의 선택자는 요소를 선택하는 데 사용됩니다. +var page = $(window); // 전체 뷰포트 선택 + +// 선택자는 CSS 선택자일 수도 있습니다. +var paragraph = $('p'); // 모든 단락 요소 선택 +var table1 = $('#table1'); // id가 'table1'인 요소 선택 +var squares = $('.square'); // 클래스가 'square'인 모든 요소 선택 +var square_p = $('p.square') // 'square' 클래스가 있는 단락 선택 + + +/////////////////////////////////// +// 2. 이벤트 및 효과 +// jQuery는 이벤트가 트리거될 때 발생하는 일을 처리하는 데 매우 좋습니다. +// 매우 일반적인 이벤트는 문서의 준비 이벤트입니다. +// 'ready' 메서드를 사용하여 요소 로드가 완료될 때까지 기다릴 수 있습니다. +$(document).ready(function(){ + // 문서가 로드될 때까지 코드가 실행되지 않습니다. +}); +// 정의된 함수를 사용할 수도 있습니다. +function onAction() { + // 이벤트가 트리거될 때 실행됩니다. +} +$('#btn').click(onAction); // 클릭 시 onAction 호출 + +// 다른 일반적인 이벤트는 다음과 같습니다: +$('#btn').dblclick(onAction); // 더블 클릭 +$('#btn').hover(onAction); // 위에 마우스를 올리면 +$('#btn').focus(onAction); // 포커스 시 +$('#btn').blur(onAction); // 포커스 잃음 +$('#btn').submit(onAction); // 제출 시 +$('#btn').select(onAction); // 요소가 선택될 때 +$('#btn').keydown(onAction); // 키를 누를 때 +$('#btn').keyup(onAction); // 키를 놓을 때 +$('#btn').keypress(onAction); // 키를 누를 때 +$('#btn').mousemove(onAction); // 마우스를 움직일 때 +$('#btn').mouseenter(onAction); // 마우스가 요소에 들어갈 때 +$('#btn').mouseleave(onAction); // 마우스가 요소를 떠날 때 + + +// 이 모든 것은 매개변수를 제공하지 않고 이벤트를 처리하는 대신 트리거할 수도 있습니다. +$('#btn').dblclick(); // 요소에서 더블 클릭 발생 + +// 선택자를 한 번만 사용하여 여러 이벤트를 처리할 수 있습니다. +$('#btn').on( + {dblclick: myFunction1} // 더블 클릭 시 트리거됨 + {blur: myFunction1} // 블러 시 트리거됨 +); + +// 일부 효과 메서드로 요소를 이동하고 숨길 수 있습니다. +$('.table').hide(); // 요소 숨기기 + +// 참고: 이러한 메서드에서 함수를 호출해도 요소가 숨겨집니다. +$('.table').hide(function(){ + // 요소가 숨겨진 다음 함수 실행 +}); + +// 선택자를 변수에 저장할 수 있습니다. +var tables = $('.table'); + +// 일부 기본 문서 조작 메서드는 다음과 같습니다: +tables.hide(); // 요소 숨기기 +tables.show(); // 요소 표시(숨기기 해제) +tables.toggle(); // 숨기기/표시 상태 변경 +tables.fadeOut(); // 페이드 아웃 +tables.fadeIn(); // 페이드 인 +tables.fadeToggle(); // 페이드 인 또는 아웃 +tables.fadeTo(0.5); // 불투명도로 페이드(0과 1 사이) +tables.slideUp(); // 위로 슬라이드 +tables.slideDown(); // 아래로 슬라이드 +tables.slideToggle(); // 위 또는 아래로 슬라이드 + +// 위의 모든 것은 속도(밀리초)와 콜백 함수를 사용합니다. +tables.hide(1000, myFunction); // 1초 숨기기 애니메이션 후 함수 + +// fadeTo에는 두 번째 매개변수로 필수 불투명도가 있습니다. +tables.fadeTo(2000, 0.1, myFunction); // 2초. 0.1 불투명도로 페이드 후 함수 + +// animate 메서드로 약간 더 고급화할 수 있습니다. +tables.animate({margin-top:"+=50", height: "100px"}, 500, myFunction); +// animate 메서드는 끝낼 css 및 값의 개체, 애니메이션을 조정하기 위한 선택적 옵션 매개변수 및 물론 콜백 함수를 사용합니다. + +/////////////////////////////////// +// 3. 조작 + +// 이것들은 효과와 비슷하지만 더 많은 것을 할 수 있습니다. +$('div').addClass('taming-slim-20'); // 모든 div에 클래스 taming-slim-20 추가 + +// 일반적인 조작 메서드 +$('p').append('Hello world'); // 요소 끝에 추가 +$('p').attr('class'); // 속성 가져오기 +$('p').attr('class', 'content'); // 속성 설정 +$('p').hasClass('taming-slim-20'); // 클래스가 있으면 true 반환 +$('p').height(); // 요소 높이 가져오기 또는 높이 설정 + + +// 많은 조작 메서드의 경우 요소에 대한 정보 가져오기는 첫 번째 일치하는 요소만 가져옵니다. +$('p').height(); // 첫 번째 'p' 태그의 높이만 가져옵니다. + +// each를 사용하여 모든 요소를 반복할 수 있습니다. +var heights = []; +$('p').each(function() { + heights.push($(this).height()); // 모든 'p' 태그 높이를 배열에 추가 +}); +``` + +## 추가 자료 + +* [Codecademy - jQuery](https://www.codecademy.com/learn/learn-jquery) "하면서 배우기" 형식의 jQuery에 대한 좋은 소개입니다. \ No newline at end of file diff --git a/ko/jsonnet.md b/ko/jsonnet.md new file mode 100644 index 0000000000..0c2bf95ae3 --- /dev/null +++ b/ko/jsonnet.md @@ -0,0 +1,136 @@ +--- +name: Jsonnet +filename: learnjsonnet.jsonnet +contributors: + - ["Huan Wang", "https://github.com/fredwangwang"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Jsonnet은 JSON을 위한 강력한 템플릿 언어입니다. 모든 유효한 JSON 문서는 유효한 Jsonnet 개체입니다. 대화형 데모/튜토리얼을 보려면 [여기](https://jsonnet.org/learning/tutorial.html)를 클릭하십시오. + +```jsonnet +// 한 줄 주석 + +/* + 여러 줄 주석 +*/ + +# 파이썬 스타일 주석도 마찬가지입니다. + +# 변수를 정의합니다. +# 변수는 사용되지 않으면 생성된 JSON에 영향을 주지 않습니다. +local num1 = 1; +local num2 = 1 + 1; +local num3 = 5 - 2; +local num4 = 9 % 5; +local num5 = 10 / 2.0; +# jsonnet은 지연 언어이므로 변수가 사용되지 않으면 평가되지 않습니다. +local num_runtime_error = 1 / 0; + +# 필드는 따옴표 없는 유효한 식별자입니다. +local obj1 = { a: 'letter a', B: 'letter B' }; + +local arr1 = ['a', 'b', 'c']; + +# 문자열 리터럴은 " 또는 '를 사용합니다. +local str1 = 'a' + 'B'; +# ||| 사이의 여러 줄 텍스트 리터럴 +# 각 줄은 공백으로 시작해야 합니다. +local str_multiline = ||| + this is a + multiline string +|||; +# %를 통해 Python 호환 문자열 서식 지정을 사용할 수 있습니다. +# |||와 결합하면 텍스트 파일 템플릿에 사용할 수 있습니다. +local str_templating = ||| + %(f1)0.3f +||| % { f1: 1.2345678 }; +assert str_templating == '1.235\n'; + +# if b then e else e. else 분기는 선택 사항이며 기본값은 null입니다. +local var1 = if 3 < 2 then "YES"; +assert var1 == null; + +local obj2 = { + # 개체 내부에 정의된 변수는 ','로 끝납니다. + local var_in_obj = 0, + + local vowels = ['a', 'e', 'i', 'o', 'u'], + local numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + + # [num]을 사용하여 배열 요소 조회 + first_vowel: vowels[0], + # Python과 같이 배열을 슬라이스할 수도 있습니다. + even_numbers: numbers[1::2], + + # 파이썬 스타일 목록 및 개체 이해도 지원됩니다. + double_numbers: [x * 2 for x in numbers], + even_numbers_map: { + # 필드 이름의 [ ] 구문은 필드 이름을 동적으로 계산하기 위한 것입니다. + [x + '_is_even']: true for x in numbers if x % 2 == 0 + }, + + nested: { + nested_field1: 'some-value', + # self는 현재 개체를 참조합니다. + # ["field-name"] 또는 .field-name을 사용하여 필드를 조회할 수 있습니다. + nested_field2: self.nested_field1, + nested_field3: self.nested_field1, + # $는 가장 바깥쪽 개체를 참조합니다. + nested_field4: $.first_vowel, + + assert self.nested_field1 == self.nested_field2, + assert self.nested_field1 == self.nested_field3, + }, + + special_field: 'EVERYTHING FEELS BAD', +}; + +local obj3 = { + local var_in_obj = 1.234, + local var_in_obj2 = { a: { b: 'c' } }, + + concat_array: [1, 2, 3] + [4], + # 문자열은 +로 연결할 수 있으며, 필요한 경우 한 피연산자를 암시적으로 문자열로 변환합니다. + concat_string: '123' + 4, + + # ==는 깊은 동등성을 테스트합니다. + equals: { a: { b: 'c', d: {} } } == var_in_obj2, + + special_field: 'this feels good', +}; + +# 개체는 +로 병합할 수 있으며, 오른쪽이 필드 충돌에서 이깁니다. +local obj4 = obj2 + obj3; +assert obj4.special_field == 'this feels good'; + +# 함수를 정의합니다. +# 함수에는 위치 매개변수, 명명된 매개변수 및 기본 인수가 있습니다. +local my_function(x, y, z=1) = x + y - z; +local num6 = my_function(7, 8, 9); +local num7 = my_function(8, z=10, y=9); +local num8 = my_function(4, 5); +# 인라인 익명 함수 +local num9 = (function(x) x * x)(3); + +local obj5 = { + # 메서드를 정의합니다. + # ::로 정의된 필드는 숨겨져 생성된 JSON에 나타나지 않습니다. + # 함수는 직렬화할 수 없으므로 숨겨야 합니다. + # 개체가 생성된 JSON에서 사용되는 경우. + is_odd(x):: x % 2 == 1, +}; +assert obj5 == {}; + +# jsonnet 문서는 개체, 목록, 숫자 또는 문자열 리터럴과 같이 무언가로 평가되어야 합니다. +"FIN" +``` + +## 추가 자료 +이 예제에서 다루지 않은 몇 가지 중요하지만 중요한 개념이 있습니다. 다음을 포함합니다: + +- 명령줄에서 변수 전달: [전체 구성 매개변수화](https://jsonnet.org/learning/tutorial.html#parameterize-entire-config) +- 다른 jsonnet 라이브러리/파일 가져오기: [가져오기](https://jsonnet.org/learning/tutorial.html#imports) +- Jsonnet의 OOP 측면에 대한 심층 예제: [객체 지향](https://jsonnet.org/learning/tutorial.html#Object-Orientation) +- 유용한 표준 라이브러리: [Stdlib](https://jsonnet.org/ref/stdlib.html) \ No newline at end of file diff --git a/ko/julia.md b/ko/julia.md new file mode 100644 index 0000000000..78a582faba --- /dev/null +++ b/ko/julia.md @@ -0,0 +1,874 @@ +--- +name: Julia +contributors: + - ["Leah Hanson", "http://leahhanson.us"] + - ["Pranit Bauva", "https://github.com/pranitbauva1997"] + - ["Daniel YC Lin", "https://github.com/dlintw"] +filename: learnjulia.jl +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Julia는 기술 컴퓨팅에 중점을 둔 새로운 동형 함수형 언어입니다. 동형 매크로, 일급 함수 및 저수준 제어의 모든 기능을 갖추고 있으면서도 Julia는 Python만큼 배우고 사용하기 쉽습니다. + +이것은 Julia 버전 1.0.0을 기반으로 합니다. + +```julia +# 한 줄 주석은 해시(파운드) 기호로 시작합니다. +#= 여러 줄 주석은 + '#='를 텍스트 앞에, '=#'를 텍스트 뒤에 + 붙여 작성할 수 있습니다. 중첩도 가능합니다. +=# + +#################################################### +## 1. 기본 데이터 유형 및 연산자 +#################################################### + +# Julia의 모든 것은 표현식입니다. + +# 몇 가지 기본 숫자 유형이 있습니다. +typeof(3) # => Int64 +typeof(3.2) # => Float64 +typeof(2 + 1im) # => Complex{Int64} +typeof(2 // 3) # => Rational{Int64} + +# 모든 일반적인 중위 연산자를 사용할 수 있습니다. +1 + 1 # => 2 +8 - 1 # => 7 +10 * 2 # => 20 +35 / 5 # => 7.0 +10 / 2 # => 5.0 # 정수 나누기는 항상 Float64를 반환합니다. +div(5, 2) # => 2 # 잘린 결과를 얻으려면 div를 사용하십시오. +5 \ 35 # => 7.0 +2^2 # => 4 # 거듭제곱, 비트 XOR 아님 +12 % 10 # => 2 + +# 괄호를 사용하여 우선 순위를 적용합니다. +(1 + 3) * 2 # => 8 + +# Julia(예를 들어 Python과 달리)는 정수 언더/오버플로가 있습니다. +10^19 # => -8446744073709551616 +# 이를 피하려면 bigint 또는 부동 소수점을 사용하십시오. +big(10)^19 # => 10000000000000000000 +1e19 # => 1.0e19 +10.0^19 # => 1.0e19 + +# 비트 연산자 +~2 # => -3 # 비트 NOT +3 & 5 # => 1 # 비트 AND +2 | 4 # => 6 # 비트 OR +xor(2, 4) # => 6 # 비트 XOR +2 >>> 1 # => 1 # 논리적 오른쪽 시프트 +2 >> 1 # => 1 # 산술적 오른쪽 시프트 +2 << 1 # => 4 # 논리적/산술적 왼쪽 시프트 + +# bitstring 함수를 사용하여 숫자의 이진 표현을 볼 수 있습니다. +bitstring(12345) +# => "0000000000000000000000000000000000000000000000000011000000111001" +bitstring(12345.0) +# => "0100000011001000000111001000000000000000000000000000000000000000" + +# 부울 값은 기본형입니다. +true +false + +# 부울 연산자 +!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 +# 비교는 Python과 같이 연결될 수 있지만 다른 많은 언어와는 다릅니다. +1 < 2 < 3 # => true +2 < 3 < 2 # => false + +# 문자열은 "로 생성됩니다. +"This is a string." + +# 문자 리터럴은 '로 작성됩니다. +'a' + +# 문자열은 UTF8로 인코딩되므로 "π" 또는 "☃"와 같은 문자열은 단일 문자 배열과 직접적으로 동일하지 않습니다. +# ASCII 문자만 포함하는 경우에만 안전하게 인덱싱할 수 있습니다. +ascii("This is a string")[1] # => 'T' +# => 'T': ASCII/Unicode U+0054 (카테고리 Lu: 대문자) +# 주의, Julia는 대부분의 언어와 달리 1부터 인덱싱합니다(MATLAB과 유사). +# 그렇지 않으면 문자열을 반복하는 것이 좋습니다(map, for 루프 등). + +# 문자열은 사전순으로 비교할 수 있습니다: +"good" > "bye" # => true +"good" == "good" # => true +"1 + 2 = 3" == "1 + 2 = $(1 + 2)" # => true + +# $(..)는 문자열 보간에 사용할 수 있습니다: +"2 + 2 = $(2 + 2)" # => "2 + 2 = 4" +# 괄호 안에 모든 Julia 표현식을 넣을 수 있습니다. + +# 인쇄는 쉽습니다. +println("I'm Julia. Nice to meet you!") # => I'm Julia. Nice to meet you! + +# 문자열을 서식 지정하는 또 다른 방법은 stdlib Printf의 printf 매크로입니다. +using Printf # 이것이 모듈을 로드(또는 가져오기)하는 방법입니다. +@printf "%d is less than %f\n" 4.5 5.3 # => 5 is less than 5.300000 + + +#################################################### +## 2. 변수 및 컬렉션 +#################################################### + +# 할당하기 전에 변수를 선언하지 않습니다. +someVar = 5 # => 5 +someVar # => 5 + +# 이전에 할당되지 않은 변수에 액세스하면 오류가 발생합니다. +try + someOtherVar # => ERROR: UndefVarError: someOtherVar not defined +catch e + println(e) +end + +# 변수 이름은 문자 또는 밑줄로 시작합니다. +# 그 후에는 문자, 숫자, 밑줄 및 느낌표를 사용할 수 있습니다. +SomeOtherVar123! = 6 # => 6 + +# 특정 유니코드 문자를 사용할 수도 있습니다. +# 여기서 ☃는 유니코드 '눈사람' 문자이며, 잘못 표시되면 http://emojipedia.org/%E2%98%83%EF%B8%8F를 참조하십시오. +☃ = 8 # => 8 +# 이것들은 상수 π와 같은 수학적 표기법에 특히 편리합니다. +2 * π # => 6.283185307179586 + +# Julia의 명명 규칙에 대한 참고 사항: +# +# * 단어 구분은 밑줄('_')로 표시할 수 있지만, 이름이 읽기 어려운 경우가 아니면 밑줄 사용을 권장하지 않습니다. +# +# * 유형 이름은 대문자로 시작하고 단어 구분은 밑줄 대신 CamelCase로 표시됩니다. +# +# * 함수 및 매크로 이름은 소문자이며 밑줄이 없습니다. +# +# * 인수를 수정하는 함수 이름은 !로 끝납니다. 이러한 함수를 때때로 변경 함수 또는 제자리 함수라고 합니다. + +# 배열은 1부터 n까지의 정수로 인덱싱된 값의 시퀀스를 저장합니다: +a = Int64[] # => 0-element Array{Int64,1} + +# 1차원 배열 리터럴은 쉼표로 구분된 값으로 작성할 수 있습니다. +b = [4, 5, 6] # => 3-element Array{Int64,1}: [4, 5, 6] +b = [4; 5; 6] # => 3-element Array{Int64,1}: [4, 5, 6] +b[1] # => 4 +b[end] # => 6 + +# 2차원 배열은 공백으로 구분된 값과 세미콜론으로 구분된 행을 사용합니다. +matrix = [1 2; 3 4] # => 2×2 Array{Int64,2}: [1 2; 3 4] + +# 특정 유형의 배열 +b = Int8[4, 5, 6] # => 3-element Array{Int8,1}: [4, 5, 6] + +# push! 및 append!를 사용하여 목록 끝에 항목 추가 +# 규칙에 따라 !가 붙은 함수 이름은 인수를 수정합니다. +push!(a, 1) # => [1] +push!(a, 2) # => [1,2] +push!(a, 4) # => [1,2,4] +push!(a, 3) # => [1,2,4,3] +append!(a, b) # => [1,2,4,3,4,5,6] + +# pop으로 끝에서 제거 +pop!(b) # => 6 +b # => [4,5] + +# 다시 넣기 +push!(b, 6) # => [4,5,6] +b # => [4,5,6] + +a[1] # => 1 # Julia는 0이 아닌 1부터 인덱싱한다는 것을 기억하십시오! + +# end는 마지막 인덱스에 대한 약어입니다. 모든 인덱싱 표현식에서 사용할 수 있습니다. +a[end] # => 6 + +# popfirst! 및 pushfirst!도 있습니다. +popfirst!(a) # => 1 +a # => [2,4,3,4,5,6] +pushfirst!(a, 7) # => [7,2,4,3,4,5,6] +a # => [7,2,4,3,4,5,6] + +# !가 붙은 함수 이름은 인수를 수정함을 나타냅니다. +arr = [5,4,6] # => 3-element Array{Int64,1}: [5,4,6] +sort(arr) # => [4,5,6] +arr # => [5,4,6] +sort!(arr) # => [4,5,6] +arr # => [4,5,6] + +# 범위를 벗어나면 BoundsError가 발생합니다. +try + a[0] + # => ERROR: BoundsError: attempt to access 7-element Array{Int64,1} at + # index [0] + # => Stacktrace: + # => [1] getindex(::Array{Int64,1}, ::Int64) at .\array.jl:731 + # => [2] top-level scope at none:0 + # => [3] ... + # => in expression starting at ...\LearnJulia.jl:180 + a[end + 1] + # => ERROR: BoundsError: attempt to access 7-element Array{Int64,1} at + # index [8] + # => Stacktrace: + # => [1] getindex(::Array{Int64,1}, ::Int64) at .\array.jl:731 + # => [2] top-level scope at none:0 + # => [3] ... + # => in expression starting at ...\LearnJulia.jl:188 +catch e + println(e) +end + +# 오류 목록은 표준 라이브러리에 있더라도 발생한 줄과 파일을 나열합니다. julia 폴더 내의 share/julia 폴더에서 이러한 파일을 찾을 수 있습니다. + +# 범위에서 배열을 초기화할 수 있습니다. +a = [1:5;] # => 5-element Array{Int64,1}: [1,2,3,4,5] +a2 = [1:5] # => 1-element Array{UnitRange{Int64},1}: [1:5] + +# 슬라이스 구문으로 범위를 볼 수 있습니다. +a[1:3] # => [1, 2, 3] +a[2:end] # => [2, 3, 4, 5] + +# splice!를 사용하여 인덱스로 배열에서 요소 제거 +arr = [3,4,5] +splice!(arr, 2) # => 4 +arr # => [3,5] + +# append!로 목록 연결 +b = [1,2,3] +append!(a, b) # => [1, 2, 3, 4, 5, 1, 2, 3] +a # => [1, 2, 3, 4, 5, 1, 2, 3] + +# in으로 목록에 있는지 확인 +in(1, a) # => true + +# length로 길이 검사 +length(a) # => 8 + +# 튜플은 불변입니다. +tup = (1, 2, 3) # => (1,2,3) +typeof(tup) # => Tuple{Int64,Int64,Int64} +tup[1] # => 1 +try + tup[1] = 3 + # => ERROR: MethodError: no method matching + # setindex!(::Tuple{Int64,Int64,Int64}, ::Int64, ::Int64) +catch e + println(e) +end + +# 많은 배열 함수는 튜플에서도 작동합니다. +length(tup) # => 3 +tup[1:2] # => (1,2) +in(2, tup) # => true + +# 튜플을 변수로 풀 수 있습니다. +a, b, c = (1, 2, 3) # => (1,2,3) +a # => 1 +b # => 2 +c # => 3 + +# 괄호를 생략해도 튜플이 생성됩니다. +d, e, f = 4, 5, 6 # => (4,5,6) +d # => 4 +e # => 5 +f # => 6 + +# 1-요소 튜플은 포함하는 값과 구별됩니다. +(1,) == 1 # => false +(1) == 1 # => true + +# 두 값을 바꾸는 것이 얼마나 쉬운지 보십시오. +e, d = d, e # => (5,4) +d # => 5 +e # => 4 + +# 사전은 매핑을 저장합니다. +emptyDict = Dict() # => Dict{Any,Any} with 0 entries + +# 리터럴을 사용하여 사전을 만들 수 있습니다. +filledDict = Dict("one" => 1, "two" => 2, "three" => 3) +# => Dict{String,Int64} with 3 entries: +# => "two" => 2, "one" => 1, "three" => 3 + +# []로 값 조회 +filledDict["one"] # => 1 + +# 모든 키 가져오기 +keys(filledDict) +# => Base.KeySet for a Dict{String,Int64} with 3 entries. Keys: +# => "two", "one", "three" +# 참고 - 사전 키는 정렬되거나 삽입한 순서대로가 아닙니다. + +# 모든 값 가져오기 +values(filledDict) +# => Base.ValueIterator for a Dict{String,Int64} with 3 entries. Values: +# => 2, 1, 3 +# 참고 - 키 순서에 대한 위와 동일합니다. + +# in, haskey를 사용하여 사전에 키가 있는지 확인 +in(("one" => 1), filledDict) # => true +in(("two" => 3), filledDict) # => false +haskey(filledDict, "one") # => true +haskey(filledDict, 1) # => false + +# 존재하지 않는 키를 조회하려고 하면 오류가 발생합니다. +try + filledDict["four"] # => ERROR: KeyError: key "four" not found +catch e + println(e) +end + +# 기본값을 제공하여 해당 오류를 피하려면 get 메서드를 사용하십시오. +# get(dictionary, key, defaultValue) +get(filledDict, "one", 4) # => 1 +get(filledDict, "four", 4) # => 4 + +# 집합을 사용하여 정렬되지 않은 고유한 값의 컬렉션을 나타냅니다. +emptySet = Set() # => Set(Any[]) +# 값으로 집합 초기화 +filledSet = Set([1, 2, 2, 3, 4]) # => Set([4, 2, 3, 1]) + +# 집합에 더 많은 값 추가 +push!(filledSet, 5) # => Set([4, 2, 3, 5, 1]) + +# 값이 집합에 있는지 확인 +in(2, filledSet) # => true +in(10, filledSet) # => false + +# 집합 교집합, 합집합 및 차집합에 대한 함수가 있습니다. +otherSet = Set([3, 4, 5, 6]) # => Set([4, 3, 5, 6]) +intersect(filledSet, otherSet) # => Set([4, 3, 5]) +union(filledSet, otherSet) # => Set([4, 2, 3, 5, 6, 1]) +setdiff(Set([1,2,3,4]), Set([2,3,5])) # => Set([4, 1]) + +# `=`를 사용한 할당은 복사 없이 동일한 값에 새 레이블을 붙입니다. +a = [1, 2, 3] +b = a +# 이제 `b`와 `a`는 동일한 값을 가리키므로 하나를 변경하면 다른 하나에 영향을 미칩니다: +a[3] = 5 +b[3] # => 5 + +# `copy()` 함수는 배열, 사전 또는 다른 컨테이너의 얕은 복사본을 만들 수 있습니다. +a = [1, 2, 3] +c = copy(a) +a[3] = 5 +c[3] # => 3 + +#################################################### +## 3. 제어 흐름 +#################################################### + +# 변수를 만들어 봅시다. +someVar = 5 + +# 다음은 if 문입니다. Julia에서는 들여쓰기가 의미가 없습니다. +if someVar > 10 + println("someVar is totally bigger than 10.") +elseif someVar < 10 # 이 elseif 절은 선택 사항입니다. + println("someVar is smaller than 10.") +else # else 절도 선택 사항입니다. + println("someVar is indeed 10.") +end +# => "some var is smaller than 10" 인쇄 + +# for 루프는 반복 가능한 항목을 반복합니다. +# 반복 가능한 유형에는 범위, 배열, 집합, 사전 및 AbstractString이 포함됩니다. +for animal = ["dog", "cat", "mouse"] + println("$animal is a mammal") + # $를 사용하여 변수 또는 표현식을 문자열에 보간할 수 있습니다. + # 이 특별한 경우 괄호가 필요하지 않습니다: $animal과 $(animal)은 동일합니다. +end +# => dog is a mammal +# => cat is a mammal +# => mouse is a mammal + +# "=" 대신 'in'을 사용할 수 있습니다. +for animal in ["dog", "cat", "mouse"] + println("$animal is a mammal") +end +# => dog is a mammal +# => cat is a mammal +# => mouse is a mammal + +for pair in Dict("dog" => "mammal", "cat" => "mammal", "mouse" => "mammal") + from, to = pair + println("$from is a $to") +end +# => mouse is a mammal +# => cat is a mammal +# => dog is a mammal + +for (k, v) in Dict("dog" => "mammal", "cat" => "mammal", "mouse" => "mammal") + println("$k is a $v") +end +# => mouse is a mammal +# => cat is a mammal +# => dog is a mammal + +# while 루프는 조건이 참인 동안 반복됩니다. +let x = 0 + while x < 4 + println(x) + x += 1 # 제자리 증가의 약어: x = x + 1 + end +end +# => 0 +# => 1 +# => 2 +# => 3 + +# try/catch 블록으로 예외 처리 +try + error("help") +catch e + println("caught it $e") +end +# => caught it ErrorException("help") + +#################################################### +## 4. 함수 +#################################################### + +# 키워드 'function'은 새 함수를 만듭니다. +# function name(arglist) +# body... +# end +function add(x, y) + println("x is $x and y is $y") + + # 함수는 마지막 문의 값을 반환합니다. + x + y +end + +add(5, 6) +# => x is 5 and y is 6 +# => 11 + +# 함수의 간결한 할당 +f_add(x, y) = x + y # => f_add (1개의 메서드가 있는 제네릭 함수) +f_add(3, 4) # => 7 + +# 함수는 튜플로 여러 값을 반환할 수도 있습니다. +fn(x, y) = x + y, x - y # => fn (1개의 메서드가 있는 제네릭 함수) +fn(3, 4) # => (7, -1) + +# 가변 개수의 위치 인수를 사용하는 함수를 정의할 수 있습니다. +function varargs(args...) + return args + # return 키워드를 사용하여 함수 어디에서나 반환 +end +# => varargs (1개의 메서드가 있는 제네릭 함수) + +varargs(1, 2, 3) # => (1,2,3) + +# ...는 스플랫이라고 합니다. +# 함수 정의에서 방금 사용했습니다. +# 함수 호출에서도 사용할 수 있으며, 배열 또는 튜플의 내용을 인수 목록으로 스플랫합니다. +add([5,6]...) # 이것은 add(5,6)와 동일합니다. + +x = (5, 6) # => (5,6) +add(x...) # 이것은 add(5,6)와 동일합니다. + + +# 선택적 위치 인수가 있는 함수를 정의할 수 있습니다. +function defaults(a, b, x=5, y=6) + return "$a $b and $x $y" +end +# => defaults (3개의 메서드가 있는 제네릭 함수) + +defaults('h', 'g') # => "h g and 5 6" +defaults('h', 'g', 'j') # => "h g and j 6" +defaults('h', 'g', 'j', 'k') # => "h g and j k" +try + defaults('h') # => ERROR: MethodError: no method matching defaults(::Char) + defaults() # => ERROR: MethodError: no method matching defaults() +catch e + println(e) +end + +# 키워드 인수를 사용하는 함수를 정의할 수 있습니다. +function keyword_args(;k1=4, name2="hello") # ; 참고 + return Dict("k1" => k1, "name2" => name2) +end +# => keyword_args (1개의 메서드가 있는 제네릭 함수) + +keyword_args(name2="ness") # => ["name2"=>"ness", "k1"=>4] +keyword_args(k1="mine") # => ["name2"=>"hello", "k1"=>"mine"] +keyword_args() # => ["name2"=>"hello", "k1"=>4] + +# 모든 종류의 인수를 동일한 함수에 결합할 수 있습니다. +function all_the_args(normalArg, optionalPositionalArg=2; keywordArg="foo") + println("normal arg: $normalArg") + println("optional arg: $optionalPositionalArg") + println("keyword arg: $keywordArg") +end +# => all_the_args (2개의 메서드가 있는 제네릭 함수) + +all_the_args(1, 3, keywordArg=4) +# => normal arg: 1 +# => optional arg: 3 +# => keyword arg: 4 + +# Julia에는 일급 함수가 있습니다. +function create_adder(x) + adder = function (y) + return x + y + end + return adder +end +# => create_adder (1개의 메서드가 있는 제네릭 함수) + +# 이것은 익명 함수를 만들기 위한 "stabby lambda syntax"입니다. +(x -> x > 2)(3) # => true + +# 이 함수는 위의 create_adder 구현과 동일합니다. +function create_adder(x) + y -> x + y +end +# => create_adder (1개의 메서드가 있는 제네릭 함수) + +# 원하는 경우 내부 함수에 이름을 지정할 수도 있습니다. +function create_adder(x) + function adder(y) + x + y + end + adder +end +# => create_adder (1개의 메서드가 있는 제네릭 함수) + +add_10 = create_adder(10) # => (::getfield(Main, Symbol("#adder#11")){Int64}) + # (1개의 메서드가 있는 제네릭 함수) +add_10(3) # => 13 + + +# 내장 고차 함수가 있습니다. +map(add_10, [1,2,3]) # => [11, 12, 13] +filter(x -> x > 5, [3, 4, 5, 6, 7]) # => [6, 7] + +# 목록 이해를 사용할 수 있습니다. +[add_10(i) for i = [1, 2, 3]] # => [11, 12, 13] +[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. 유형 +#################################################### + +# Julia에는 유형 시스템이 있습니다. +# 모든 값에는 유형이 있습니다. 변수 자체에는 유형이 없습니다. +# `typeof` 함수를 사용하여 값의 유형을 얻을 수 있습니다. +typeof(5) # => Int64 + +# 유형은 일급 값입니다. +typeof(Int64) # => DataType +typeof(DataType) # => DataType +# DataType은 자신을 포함하여 유형을 나타내는 유형입니다. + +# 유형은 문서, 최적화 및 디스패치에 사용됩니다. +# 정적으로 확인되지 않습니다. + +# 사용자는 유형을 정의할 수 있습니다. +# 다른 언어의 레코드 또는 구조체와 같습니다. +# 새 유형은 `struct` 키워드를 사용하여 정의됩니다. + +# struct Name +# field::OptionalType +# ... +# end +struct Tiger + taillength::Float64 + coatcolor # 유형 주석을 포함하지 않는 것은 `::Any`와 동일합니다. +end + +# 기본 생성자의 인수는 유형의 속성이며, 정의에 나열된 순서대로입니다. +tigger = Tiger(3.5, "orange") # => Tiger(3.5,"orange") + +# 유형은 해당 유형의 값에 대한 생성자 함수 역할을 합니다. +sherekhan = typeof(tigger)(5.6, "fire") # => Tiger(5.6,"fire") + +# 이러한 구조체 스타일 유형을 구체적인 유형이라고 합니다. +# 인스턴스화할 수 있지만 하위 유형을 가질 수 없습니다. +# 다른 종류의 유형은 추상 유형입니다. + +# abstract Name +abstract type Cat end # 이름과 유형 계층의 지점일 뿐입니다. + +# 추상 유형은 인스턴스화할 수 없지만 하위 유형을 가질 수 있습니다. +# 예를 들어, Number는 추상 유형입니다. +subtypes(Number) # => 2-element Array{Any,1}: + # => Complex + # => Real +subtypes(Cat) # => 0-element Array{Any,1} + +# AbstractString은 이름에서 알 수 있듯이 추상 유형이기도 합니다. +subtypes(AbstractString) # => 4-element Array{Any,1}: + # => String + # => SubString + # => SubstitutionString + # => Test.GenericString + +# 모든 유형에는 상위 유형이 있습니다. `supertype` 함수를 사용하여 가져옵니다. +typeof(5) # => Int64 +supertype(Int64) # => Signed +supertype(Signed) # => Integer +supertype(Integer) # => Real +supertype(Real) # => Number +supertype(Number) # => Any +supertype(supertype(Signed)) # => Real +supertype(Any) # => Any +# Int64를 제외한 이러한 유형은 모두 추상적입니다. +typeof("fire") # => String +supertype(String) # => AbstractString +# 마찬가지로 String도 마찬가지입니다. +supertype(SubString) # => AbstractString + +# <:는 하위 유형 연산자입니다. +struct Lion <: Cat # Lion은 Cat의 하위 유형입니다. + maneColor + roar::AbstractString +end + +# 유형에 대한 더 많은 생성자를 정의할 수 있습니다. +# 유형과 동일한 이름의 함수를 정의하고 기존 생성자를 호출하여 올바른 유형의 값을 얻습니다. +Lion(roar::AbstractString) = Lion("green", roar) +# 이것은 유형 정의 외부에 있으므로 외부 생성자입니다. + +struct Panther <: Cat # Panther도 Cat의 하위 유형입니다. + eyeColor + Panther() = new("green") + # Panther는 이 생성자만 가지며 기본 생성자는 없습니다. +end +# Panther와 같이 내부 생성자를 사용하면 유형의 값을 만드는 방법을 제어할 수 있습니다. +# 가능하면 내부 생성자 대신 외부 생성자를 사용해야 합니다. + +#################################################### +## 6. 다중 디스패치 +#################################################### + +# Julia에서 모든 명명된 함수는 제네릭 함수입니다. +# 이것은 많은 작은 메서드로 구성됨을 의미합니다. +# Lion의 각 생성자는 제네릭 함수 Lion의 메서드입니다. + +# 비생성자 예제로 meow 함수를 만들어 보겠습니다: + +# Lion, Panther, Tiger에 대한 정의 +function meow(animal::Lion) + animal.roar # 점 표기법을 사용하여 유형 속성에 액세스 +end + +function meow(animal::Panther) + "grrr" +end + +function meow(animal::Tiger) + "rawwwr" +end + +# meow 함수 테스트 +meow(tigger) # => "rawwwr" +meow(Lion("brown", "ROAAR")) # => "ROAAR" +meow(Panther()) # => "grrr" + +# 로컬 유형 계층 검토 +Tiger <: Cat # => false +Lion <: Cat # => true +Panther <: Cat # => true + +# Cat을 사용하는 함수 정의 +function pet_cat(cat::Cat) + println("The cat says $(meow(cat))") +end +# => pet_cat (1개의 메서드가 있는 제네릭 함수) + +pet_cat(Lion("42")) # => The cat says 42 +try + pet_cat(tigger) # => ERROR: MethodError: no method matching pet_cat(::Tiger) +catch e + println(e) +end + +# OO 언어에서는 단일 디스패치가 일반적입니다. +# 이것은 첫 번째 인수의 유형에 따라 메서드가 선택됨을 의미합니다. +# Julia에서는 모든 인수 유형이 최상의 메서드를 선택하는 데 기여합니다. + +# 차이점을 보기 위해 더 많은 인수가 있는 함수를 정의해 보겠습니다. +function fight(t::Tiger, c::Cat) + println("The $(t.coatcolor) tiger wins!") +end +# => fight (1개의 메서드가 있는 제네릭 함수) + +fight(tigger, Panther()) # => The orange tiger wins! +fight(tigger, Lion("ROAR")) # => The orange tiger wins! + +# Cat이 구체적으로 Lion일 때 동작 변경 +fight(t::Tiger, l::Lion) = println("The $(l.maneColor)-maned lion wins!") +# => fight (2개의 메서드가 있는 제네릭 함수) + +fight(tigger, Panther()) # => The orange tiger wins! +fight(tigger, Lion("ROAR")) # => The green-maned lion wins! + +# 싸우기 위해 Tiger가 필요하지 않습니다. +fight(l::Lion, c::Cat) = println("The victorious cat says $(meow(c))") +# => fight (3개의 메서드가 있는 제네릭 함수) + +fight(Lion("balooga!"), Panther()) # => The victorious cat says grrr +try + fight(Panther(), Lion("RAWR")) + # => ERROR: MethodError: no method matching fight(::Panther, ::Lion) + # => Closest candidates are: + # => fight(::Tiger, ::Lion) at ... + # => fight(::Tiger, ::Cat) at ... + # => fight(::Lion, ::Cat) at ... + # => ... +catch e + println(e) +end + +# 고양이가 먼저 가도록 하십시오. +fight(c::Cat, l::Lion) = println("The cat beats the Lion") +# => fight (4개의 메서드가 있는 제네릭 함수) + +# 이 경고는 다음에서 어떤 싸움이 호출될지 불분명하기 때문입니다: +try + fight(Lion("RAR"), Lion("brown", "rarrr")) + # => ERROR: MethodError: fight(::Lion, ::Lion) is ambiguous. Candidates: + # => fight(c::Cat, l::Lion) in Main at ... + # => fight(l::Lion, c::Cat) in Main at ... + # => Possible fix, define + # => fight(::Lion, ::Lion) + # => ... +catch e + println(e) +end +# 결과는 다른 버전의 Julia에서 다를 수 있습니다. + +fight(l::Lion, l2::Lion) = println("The lions come to a tie") +# => fight (5개의 메서드가 있는 제네릭 함수) +fight(Lion("RAR"), Lion("brown", "rarrr")) # => The lions come to a tie + + +# 내부 +# 생성된 llvm 및 어셈블리 코드를 볼 수 있습니다. + +square_area(l) = l * l # square_area (1개의 메서드가 있는 제네릭 함수) + +square_area(5) # => 25 + +# square_area에 정수를 공급하면 어떻게 됩니까? +code_native(square_area, (Int32,), syntax = :intel) + # .text + # ; Function square_area { + # ; Location: REPL[116]:1 # Prologue + # push rbp + # mov rbp, rsp + # ; Function *; { + # ; Location: int.jl:54 + # imul ecx, ecx # l을 제곱하고 결과를 ECX에 저장 + # ;} + # mov eax, ecx + # pop rbp # 이전 기본 포인터 복원 + # ret # 결과는 여전히 EAX에 있습니다. + # nop dword ptr [rax + rax] + # ;} + +code_native(square_area, (Float32,), syntax = :intel) + # .text + # ; Function square_area { + # ; Location: REPL[116]:1 + # push rbp + # mov rbp, rsp + # ; Function *; { + # ; Location: float.jl:398 + # vmulss xmm0, xmm0, xmm0 # 스칼라 단정밀도 곱셈 (AVX) + # ;} + # pop rbp + # ret + # nop word ptr [rax + rax] + # ;} + +code_native(square_area, (Float64,), syntax = :intel) + # .text + # ; Function square_area { + # ; Location: REPL[116]:1 + # push rbp + # mov rbp, rsp + # ; Function *; { + # ; Location: float.jl:399 + # vmulsd xmm0, xmm0, xmm0 # 스칼라 배정밀도 곱셈 (AVX) + # ;} + # pop rbp + # ret + # nop word ptr [rax + rax] + # ;} + +# julia는 인수 중 하나가 부동 소수점인 경우 부동 소수점 명령을 사용합니다. +# 원의 면적을 계산해 보겠습니다. +circle_area(r) = pi * r * r # circle_area (1개의 메서드가 있는 제네릭 함수) +circle_area(5) # 78.53981633974483 + +code_native(circle_area, (Int32,), syntax = :intel) + # .text + # ; Function circle_area { + # ; Location: REPL[121]:1 + # push rbp + # mov rbp, rsp + # ; Function *; { + # ; Location: operators.jl:502 + # ; Function *; { + # ; Location: promotion.jl:314 + # ; Function promote; { + # ; Location: promotion.jl:284 + # ; Function _promote; { + # ; Location: promotion.jl:261 + # ; Function convert; { + # ; Location: number.jl:7 + # ; Function Type; { + # ; Location: float.jl:60 + # vcvtsi2sd xmm0, xmm0, ecx # 메모리에서 정수(r) 로드 + # movabs rax, 497710928 # pi 로드 + # ;}}}}} + # ; Function *; { + # ; Location: float.jl:399 + # vmulsd xmm1, xmm0, qword ptr [rax] # pi * r + # vmulsd xmm0, xmm1, xmm0 # (pi * r) * r + # ;}} + # pop rbp + # ret + # nop dword ptr [rax] + # ;} + +code_native(circle_area, (Float64,), syntax = :intel) + # .text + # ; Function circle_area { + # ; Location: REPL[121]:1 + # push rbp + # mov rbp, rsp + # movabs rax, 497711048 + # ; Function *; { + # ; Location: operators.jl:502 + # ; Function *; { + # ; Location: promotion.jl:314 + # ; Function *; { + # ; Location: float.jl:399 + # vmulsd xmm1, xmm0, qword ptr [rax] + # ;}}} + # ; Function *; { + # ; Location: float.jl:399 + # vmulsd xmm0, xmm1, xmm0 + # ;} + # pop rbp + # ret + # nop word ptr [rax + rax] + # ;} +``` + +## 추가 자료 + +[Julia 문서](https://docs.julialang.org/)에서 더 자세한 내용을 얻을 수 있습니다. + +Julia에 대한 도움을 얻을 수 있는 가장 좋은 곳은 (매우 친절한) [Discourse 포럼](https://discourse.julialang.org/)입니다. \ No newline at end of file diff --git a/ko/kdb+.md b/ko/kdb+.md new file mode 100644 index 0000000000..d6cf98af32 --- /dev/null +++ b/ko/kdb+.md @@ -0,0 +1,752 @@ +--- +name: kdb+ +contributors: + - ["Matt Doherty", "https://github.com/picodoc"] + - ["Jonny Press", "https://github.com/jonnypress"] +filename: learnkdb.q +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +q 언어와 그 데이터베이스 구성 요소인 kdb+는 Arthur Whitney가 개발하여 2003년에 Kx 시스템에서 출시했습니다. q는 APL의 후손으로, "C 계열" 언어 배경을 가진 사람에게는 매우 간결하고 약간 이상하게 보입니다. 표현력이 풍부하고 벡터 지향적인 특성 덕분에 대량의 데이터에 대한 복잡한 계산을 수행하는 데 적합합니다(동시에 [코드 골프](https://en.wikipedia.org/wiki/Code_golf)를 어느 정도 장려하기도 합니다). 언어의 기본 구조는 객체가 아니라 목록이며, 테이블은 목록의 컬렉션으로 구축됩니다. 이는 대부분의 전통적인 RDBMS 시스템과 달리 테이블이 열 지향적임을 의미합니다. 이 언어에는 인메모리 및 디스크 데이터베이스가 내장되어 있어 상당한 유연성을 제공합니다. kdb+는 대규모 시계열 데이터 세트를 저장, 분석, 처리 및 검색하기 위해 금융계에서 가장 널리 사용됩니다. + +*q*와 *kdb+*라는 용어는 일반적으로 서로 바꿔 사용할 수 있습니다. 둘은 분리할 수 없으므로 이 구분은 실제로 유용하지 않습니다. + +kdb+에 대해 더 자세히 알아보려면 [KX 커뮤니티 포럼](https://learninghub.kx.com/forums/) 또는 [TorQ kdb+](https://groups.google.com/forum/#!forum/kdbtorq) 그룹에 가입할 수 있습니다. + +```q +/ 한 줄 주석은 슬래시로 시작합니다. +/ 이것들은 공백 문자가 하나 이상 있는 경우 텍스트 왼쪽에 있는 한 인라인으로도 사용할 수 있습니다. +/ + 한 줄에 슬래시가 있으면 여러 줄 주석이 시작됩니다. + 그리고 한 줄에 백슬래시가 있으면 종료됩니다. +\ + +/ 빈 디렉토리에서 이 파일을 실행하십시오. + + +//////////////////////////////////// +// 기본 연산자 및 데이터 유형 // +//////////////////////////////////// + +/ 정수가 있으며, 기본적으로 8바이트입니다. +3 / => 3 + +/ 그리고 부동 소수점, 표준으로 8바이트입니다. 후행 f는 int와 구별됩니다. +3.0 / => 3f + +/ 4바이트 숫자 유형은 후행 문자로도 지정할 수 있습니다. +3i / => 3i +3.0e / => 3e + +/ 수학은 대부분 예상대로입니다. +1+1 / => 2 +8-1 / => 7 +10*2 / => 20 +/ 나눗셈은 슬래시(/) 대신 백분율(%)을 사용합니다. +35%5 / => 7f (나눗셈 결과는 항상 부동 소수점입니다) + +/ 정수 나눗셈에는 div 키워드가 있습니다. +4 div 3 / => 1 + +/ 모듈로도 키워드를 사용합니다. 백분율(%)이 사용되었기 때문입니다. +4 mod 3 / => 1 + +/ 그리고 거듭제곱... +2 xexp 4 / => 16 + +/ ...그리고 잘라내기... +floor 3.14159 / => 3 + +/ ...절대값 구하기... +abs -3.14159 / => 3.14159 +/ ...그리고 다른 많은 것들 +/ 자세한 내용은 http://code.kx.com/q/ref/를 참조하십시오. + +/ q에는 연산자 우선 순위가 없으며, 모든 것이 오른쪽에서 왼쪽으로 평가됩니다. +/ 따라서 이러한 결과는 익숙해지는 데 시간이 걸릴 수 있습니다. +2*1+1 / => 4 / (기억해야 할 연산자 우선 순위 테이블 없음!) + +/ 괄호로 우선 순위를 수정할 수 있습니다('정상' 결과 복원). +(2*1)+1 / => 3 + +/ 할당은 등호(=) 대신 콜론(:)을 사용합니다. +/ 할당 전에 변수를 선언할 필요가 없습니다. +a:3 +a / => 3 + +/ 변수는 인라인으로도 할당할 수 있습니다. +/ 이것은 전달되는 값에 영향을 주지 않습니다. +c:3+b:2+a:1 / (데이터는 오른쪽에서 왼쪽으로 "흐릅니다") +a / => 1 +b / => 3 +c / => 6 + +/ 제자리 연산도 예상대로입니다. +a+:2 +a / => 3 + +/ q에는 "true" 또는 "false" 키워드가 없습니다. +/ 부울 값은 비트 값 뒤에 b가 오는 것으로 표시됩니다. +1b / => true 값 +0b / => false 값 + +/ 같음 비교는 등호(=)를 사용합니다(할당에 필요하지 않으므로). +1=1 / => 1b +2=1 / => 0b + +/ 부등호는 <>를 사용합니다. +1<>1 / => 0b +2<>1 / => 1b + +/ 다른 비교는 예상대로입니다. +1<2 / => 1b +1>2 / => 0b +2<=2 / => 1b +2>=2 / => 1b + +/ 비교는 유형에 대해 엄격하지 않습니다... +42=42.0 / => 1b + +/ ...일치 연산자(~)를 사용하지 않는 한 +/ 엔티티가 동일한 경우에만 true를 반환합니다. +42~42.0 / => 0b + +/ not 연산자는 기본 값이 0이면 true를 반환합니다. +not 0b / => 1b +not 1b / => 0b +not 42 / => 0b +not 0.0 / => 1b + +/ max 연산자(|)는 부울에 대해 논리적 "or"로 축소됩니다. +42|2.0 / => 42f +1b|0b / => 1b + +/ min 연산자(&)는 부울에 대해 논리적 "and"로 축소됩니다. +42&2.0 / => 2f +1b&0b / => 0b + +/ q는 문자 데이터를 저장하는 두 가지 방법을 제공합니다. +/ q의 문자는 단일 바이트에 저장되며 큰따옴표(")를 사용합니다. +ch:"a" +/ 문자열은 단순히 문자 목록입니다(나중에 목록에 대해 자세히 설명). +str:"This is a string" +/ 이스케이프 문자는 정상적으로 작동합니다. +str:"This is a string with \"quotes\"" + +/ 문자 데이터는 백틱(`)을 사용하여 기호로도 저장할 수 있습니다. +symbol:`sym +/ 기호는 목록이 아니며, 열거형입니다. +/ q 프로세스는 내부적으로 문자열 벡터를 저장합니다. +/ 기호는 이 벡터에 대해 열거됩니다. +/ 이것은 상수 너비이므로 공간 및 속도 효율이 더 높을 수 있습니다. + +/ string 함수는 문자열로 변환합니다. +string `symbol / => "symbol" +string 1.2345 / => "1.2345" + +/ q에는 시간 유형이 있습니다... +t:01:00:00.000 +/ 날짜 유형... +d:2015.12.25 +/ 그리고 datetime 유형(다른 시간 유형 중) +dt:2015.12.25D12:00:00.000000000 + +/ 이것들은 쉬운 조작을 위해 일부 산술을 지원합니다. +dt + t / => 2015.12.25D13:00:00.000000000 +t - 00:10:00.000 / => 00:50:00.000 +/ 그리고 점 표기법을 사용하여 분해할 수 있습니다. +d.year / => 2015i +d.mm / => 12i +d.dd / => 25i +/ 자세한 내용은 http://code.kx.com/q4m3/2_Basic_Data_Types_Atoms/#25-temporal-data를 참조하십시오. + +/ q에는 무한대 값도 있으므로 0으로 나누어도 오류가 발생하지 않습니다. +1%0 / => 0w +-1%0 / => -0w + +/ 그리고 누락된 값을 나타내는 null 유형 +0N / => null int +0n / => null float +/ 자세한 내용은 http://code.kx.com/q4m3/2_Basic_Data_Types_Atoms/#27-nulls를 참조하십시오. + +/ q에는 표준 제어 구조가 있습니다. +/ if는 예상대로입니다(;는 조건과 지침을 구분합니다). +if[1=1;a:"hi"] +a / => "hi" +/ if-else는 $(그리고 if와 달리 값을 반환함)를 사용합니다. +$[1=0;a:"hi";a:"bye"] / => "bye" +a / => "bye" +/ if-else는 ;로 구분된 인수를 추가하여 여러 절로 확장할 수 있습니다. +$[1=0;a:"hi";0=1;a:"bye";a:"hello again"] +a / => "hello again" + + +//////////////////////////////////// +//// 데이터 구조 //// +//////////////////////////////////// + +/ q는 객체 지향 언어가 아닙니다. +/ 대신 복잡성은 정렬된 목록을 통해 구축됩니다. +/ 그리고 이를 상위 순서 구조인 사전 및 테이블에 매핑합니다. + +/ 목록(또는 원하는 경우 배열)은 간단한 정렬된 컬렉션입니다. +/ 괄호()와 세미콜론(;)으로 정의됩니다. +(1;2;3) / => 1 2 3 +(-10.0;3.14159e;1b;`abc;"c") +/ => -10f +/ => 3.14159e +/ => 1b +/ => `abc +/ => "c" (혼합 유형 목록은 여러 줄에 표시됩니다) +((1;2;3);(4;5;6);(7;8;9)) +/ => 1 2 3 +/ => 4 5 6 +/ => 7 8 9 + +/ 균일한 유형의 목록은 더 간결하게 정의할 수도 있습니다. +1 2 3 / => 1 2 3 +`list`of`syms / => `list`of`syms +`list`of`syms ~ (`list;`of;`syms) / => 1b + +/ 목록 길이 +count (1;2;3) / => 3 +count "I am a string" / => 13 (문자열은 문자 목록입니다) + +/ 빈 목록은 괄호로 정의됩니다. +l:() +count l / => 0 + +/ 단순 변수와 단일 항목 목록은 동일하지 않습니다. +/ 괄호 구문은 단일 항목 목록을 만들 수 없습니다(우선 순위를 나타냄). +(1)~1 / => 1b +/ 단일 항목 목록은 enlist를 사용하여 만들 수 있습니다. +singleton:enlist 1 +/ 또는 빈 목록에 추가 +singleton:(),1 +1~(),1 / => 0b + +/ 추가에 대해 말하자면, 쉼표(,)가 사용되며 더하기(+)가 아닙니다. +1 2 3,4 5 6 / => 1 2 3 4 5 6 +"hello ","there" / => "hello there" + +/ 인덱싱은 대괄호 []를 사용합니다. +l:1 2 3 4 +l[0] / => 1 +l[1] / => 2 +/ 범위를 벗어난 인덱싱은 오류 대신 null 값을 반환합니다. +l[5] / => 0N +/ 그리고 인덱싱된 할당 +l[0]:5 +l / => 5 2 3 4 + +/ 목록은 인덱싱 및 인덱싱된 할당에도 사용할 수 있습니다. +l[1 3] / => 2 4 +l[1 3]: 1 3 +l / => 5 1 3 3 + +/ 목록은 유형이 없거나 혼합 유형일 수 있습니다. +l:(1;2;`hi) +/ 하지만 균일하게 유형이 지정되면 q는 이를 강제합니다. +l[2]:3 +l / => 1 2 3 +l[2]:`hi / 유형 오류 발생 +/ 이것은 목록을 테이블 열로 사용하는 맥락에서 의미가 있습니다(나중에 자세히 설명). + +/ 중첩된 목록의 경우 깊이에서 인덱싱할 수 있습니다. +l:((1;2;3);(4;5;6);(7;8;9)) +l[1;1] / => 5 + +/ 인덱스를 생략하여 전체 행 또는 열을 반환할 수 있습니다. +l[;1] / => 2 5 8 +l[1;] / => 4 5 6 + +/ 이전 섹션에서 언급한 모든 함수는 목록에서 기본적으로 작동합니다. +1+(1;2;3) / => 2 3 4 (단일 변수 및 목록) +(1;2;3) - (3;2;1) / => -2 0 2 (목록 및 목록) + +/ 그리고 목록을 위해 특별히 설계된 더 많은 것들이 있습니다. +avg 1 2 3 / => 2f +sum 1 2 3 / => 6 +sums 1 2 3 / => 1 3 6 (누적 합계) +last 1 2 3 / => 3 +1 rotate 1 2 3 / => 2 3 1 +/ 등 +/ 이러한 함수를 사용하여 목록을 조작하는 것은 언어의 힘과 표현력의 많은 부분을 차지합니다. + +/ Take (#), drop (_) 및 find (?)도 목록 작업에 유용합니다. +l:1 2 3 4 5 6 7 8 9 +l:1+til 9 / til은 범위를 생성하는 데 유용한 바로 가기입니다. +/ 처음 5개 요소 가져오기 +5#l / => 1 2 3 4 5 +/ 처음 5개 삭제 +5_l / => 6 7 8 9 +/ 마지막 5개 가져오기 +-5#l / => 5 6 7 8 9 +/ 마지막 5개 삭제 +-5_l / => 1 2 3 4 +/ 4의 첫 번째 발생 찾기 +l?4 / => 3 +l[3] / => 4 + +/ q의 사전은 목록의 일반화입니다. +/ 목록을 다른 목록(동일한 길이)에 매핑합니다. +/ 느낌표(!) 기호는 사전을 정의하는 데 사용됩니다. +d:(`a;`b;`c)!(1;2;3) +/ 또는 간결한 목록 구문으로 더 간단하게 +d:`a`b`c!1 2 3 +/ 키워드 키는 첫 번째 목록을 반환합니다. +key d / => `a`b`c +/ 그리고 값은 두 번째 +value d / => 1 2 3 + +/ 인덱싱은 목록과 동일합니다. +/ 첫 번째 목록을 위치 대신 키로 사용 +d[`a] / => 1 +d[`b] / => 2 + +/ 할당과 마찬가지로 +d[`c]:4 +d +/ => a| 1 +/ => b| 2 +/ => c| 4 + +/ 산술 및 비교는 목록과 마찬가지로 기본적으로 작동합니다. +e:(`a;`b;`c)!(2;3;4) +d+e +/ => a| 3 +/ => b| 5 +/ => c| 8 +d-2 +/ => a| -1 +/ => b| 0 +/ => c| 2 +d > (1;1;1) +/ => a| 0 +/ => b| 1 +/ => c| 1 + +/ 그리고 take, drop 및 find 연산자는 놀랍게도 유사합니다. +`a`b#d +/ => a| 1 +/ => b| 2 +`a`b _ d +/ => c| 4 +d?2 +/ => `b + +/ q의 테이블은 기본적으로 사전의 하위 집합입니다. +/ 테이블은 모든 값이 동일한 길이의 목록이어야 하는 사전입니다. +/ 따라서 q의 테이블은 열 지향적입니다(대부분의 RDBMS와 달리). +/ flip 키워드는 사전을 테이블로 변환하는 데 사용됩니다. +/ 즉, 인덱스를 뒤집습니다. +flip `c1`c2`c3!(1 2 3;4 5 6;7 8 9) +/ => c1 c2 c3 +/ => -------- +/ => 1 4 7 +/ => 2 5 8 +/ => 3 6 9 +/ 이 구문을 사용하여 테이블을 정의할 수도 있습니다. +t:([]c1:1 2 3;c2:4 5 6;c3:7 8 9) +t +/ => c1 c2 c3 +/ => -------- +/ => 1 4 7 +/ => 2 5 8 +/ => 3 6 9 + +/ 테이블은 사전 및 목록과 유사한 방식으로 인덱싱하고 조작할 수 있습니다. +t[`c1] +/ => 1 2 3 +/ 테이블 행은 사전으로 반환됩니다. +t[1] +/ => c1| 2 +/ => c2| 5 +/ => c3| 8 + +/ meta는 테이블 유형 정보를 반환합니다. +meta t +/ => c | t f a +/ => --| ----- +/ => c1| j +/ => c2| j +/ => c3| j +/ 이제 목록에서 유형이 강제되는 이유를 알 수 있습니다(열 유형을 보호하기 위해). +t[1;`c1]:3 +t[1;`c1]:3.0 / 유형 오류 발생 + +/ 대부분의 기존 데이터베이스에는 기본 키 열이 있습니다. +/ q에는 키 테이블이 있으며, 키 열을 포함하는 한 테이블이 느낌표(!)를 사용하여 다른 테이블에 매핑됩니다. +k:([]id:1 2 3) +k!t +/ => id| c1 c2 c3 +/ => --| -------- +/ => 1 | 1 4 7 +/ => 2 | 3 5 8 +/ => 3 | 3 6 9 + +/ 키 테이블을 정의하기 위해 이 바로 가기를 사용할 수도 있습니다. +kt:([id:1 2 3]c1:1 2 3;c2:4 5 6;c3:7 8 9) + +/ 그런 다음 이 키를 기반으로 레코드를 검색할 수 있습니다. +kt[1] +/ => c1| 1 +/ => c2| 4 +/ => c3| 7 +kt[`id!1] +/ => c1| 1 +/ => c2| 4 +/ => c3| 7 + + +//////////////////////////////////// +//////// 함수 //////// +//////////////////////////////////// + +/ q에서 함수는 입력을 출력에 매핑하는 수학적 맵과 유사합니다. +/ 중괄호 {}는 함수 정의에 사용됩니다. +/ 그리고 대괄호 []는 호출에 사용됩니다(목록 인덱싱과 마찬가지로). +/ 매우 최소한의 함수 +f:{x+x} +f[2] / => 4 + +/ 함수는 익명일 수 있으며 정의 지점에서 호출될 수 있습니다. +{x+x}[2] / => 4 + +/ 기본적으로 마지막 표현식이 반환됩니다. +/ 콜론(:)을 사용하여 반환을 지정할 수 있습니다. +{x+x}[2] / => 4 +{:x+x}[2] / => 4 +/ 세미콜론(;)은 표현식을 구분합니다. +{r:x+x;:r}[2] / => 4 + +/ 함수 인수는 명시적으로 지정할 수 있습니다(세미콜론으로 구분). +{[arg1;arg2] arg1+arg2}[1;2] / => 3 +/ 또는 생략하면 x, y 및 z로 기본 설정됩니다. +{x+y+z}[1;2;3] / => 6 + +/ 내장 함수는 다르지 않으며 동일한 방식으로 호출할 수 있습니다([] 사용). ++[1;2] / => 3 +<[1;2] / => 1b + +/ 함수는 q에서 일급이므로 반환하거나 목록에 저장하는 등의 작업을 할 수 있습니다. +{:{x+y}}[] / => {x+y} +(1;"hi";{x+y}) +/ => 1 +/ => "hi" +/ => {x+y} + +/ 사용자 지정 q 함수에는 오버로딩 및 키워드 인수가 없습니다. +/ 그러나 단일 인수로 사전을 사용하면 이를 극복할 수 있습니다. +/ 선택적 인수 또는 다른 기능을 허용합니다. +d:`arg1`arg2`arg3!(1.0;2;"my function argument") +{x[`arg1]+x[`arg2]}[d] / => 3f + +/ q의 함수는 전역 범위를 봅니다. +a:1 +{:a}[] / => 1 + +/ 그러나 로컬 범위는 이를 가립니다. +a:1 +{a:2;:a}[] / => 2 +a / => 1 + +/ 함수는 중첩된 범위를 볼 수 없습니다(로컬 및 전역만). +{local:1;{:local}[]}[] / 로컬이 내부 함수에 정의되지 않았으므로 오류 발생 + +/ 함수는 하나 이상의 인수를 고정할 수 있습니다(프로젝션). +f:+[4] +f[4] / => 8 +f[5] / => 9 +f[6] / => 10 + + +//////////////////////////////////// +////////// q-sql ////////// +//////////////////////////////////// + +/ q에는 표준 SQL과 유사한 테이블 조작을 위한 자체 구문이 있습니다. +/ 여기에는 select, insert, update 등과 같은 일반적인 용의자가 포함됩니다. +/ 그리고 일반적으로 사용할 수 없는 몇 가지 새로운 기능 +/ q-sql에는 두 가지 중요한 차이점이 있습니다(구문 제외): +/ - q 테이블에는 잘 정의된 레코드 순서가 있습니다. +/ - 테이블은 열 컬렉션으로 저장됩니다. +/ (따라서 벡터화된 열 작업이 빠릅니다) +/ q-sql에 대한 전체 설명은 이 소개 범위를 약간 벗어납니다. +/ 따라서 시작하는 데 충분한 기본 사항만 다룰 것입니다. + +/ 먼저 테이블을 정의합니다. +t:([]name:`Arthur`Thomas`Polly;age:35 32 52;height:180 175 160;sex:`m`m`f) + +/ SELECT * FROM t와 동일 +select from t / (소문자여야 하며 와일드카드는 필요하지 않음) +/ => name age height sex +/ => --------------------- +/ => Arthur 35 180 m +/ => Thomas 32 175 m +/ => Polly 52 160 f + +/ 특정 열 선택 +select name,age from t +/ => name age +/ => ---------- +/ => Arthur 35 +/ => Thomas 32 +/ => Polly 52 + +/ 그리고 이름을 지정합니다(표준 SQL에서 AS를 사용하는 것과 동일). +select charactername:name, currentage:age from t +/ => charactername currentage +/ => ------------------------ +/ => Arthur 35 +/ => Thomas 32 +/ => Polly 52 + +/ 이 SQL 구문은 q 언어와 통합됩니다. +/ 따라서 q는 SQL 문에서 원활하게 사용할 수 있습니다. +select name, feet:floor height*0.032, inches:12*(height*0.032) mod 1 from t +/ => name feet inches +/ => ------------------ +/ => Arthur 5 9.12 +/ => Thomas 5 7.2 +/ => Polly 5 1.44 + +/ 사용자 지정 함수 포함 +select name, growth:{[h;a]h%a}[height;age] from t +/ => name growth +/ => --------------- +/ => Arthur 5.142857 +/ => Thomas 5.46875 +/ => Polly 3.076923 + +/ where 절에는 쉼표로 구분된 여러 문이 포함될 수 있습니다. +select from t where age>33,height>175 +/ => name age height sex +/ => --------------------- +/ => Arthur 35 180 m + +/ where 문은 순차적으로 실행됩니다(논리적 AND와 동일하지 않음). +select from t where age<40,height=min height +/ => name age height sex +/ => --------------------- +/ => Thomas 32 175 m +select from t where (age<40)&(height=min height) +/ => name age height sex +/ => ------------------- + +/ by 절은 select와 from 사이에 있으며 +/ SQL의 GROUP BY와 동일합니다. +select avg height by sex from t +/ => sex| height +/ => ---| ------ +/ => f | 160 +/ => m | 177.5 + +/ 집계 함수가 지정되지 않은 경우 last가 가정됩니다. +select by sex from t +/ => sex| name age height +/ => ---| ----------------- +/ => f | Polly 52 160 +/ => m | Thomas 32 175 + +/ Update는 select와 동일한 기본 형식을 가집니다. +update sex:`male from t where sex=`m +/ => name age height sex +/ => ---------------------- +/ => Arthur 35 180 male +/ => Thomas 32 175 male +/ => Polly 52 160 f + +/ 삭제와 마찬가지로 +delete from t where sex=`m +/ => name age height sex +/ => -------------------- +/ => Polly 52 160 f + +/ 이러한 sql 작업은 제자리에서 수행되지 않습니다. +t +/ => name age height sex +/ => --------------------- +/ => Arthur 35 180 m +/ => Thomas 32 175 m +/ => Polly 52 160 f + +/ 그러나 Insert는 제자리에서 수행되며, 테이블 이름과 새 데이터를 사용합니다. +`t insert (`John;25;178;`m) / => ,3 +t +/ => name age height sex +/ => --------------------- +/ => Arthur 35 180 m +/ => Thomas 32 175 m +/ => Polly 52 160 f +/ => John 25 178 m + +/ Upsert는 유사합니다(하지만 제자리일 필요는 없음). +t upsert (`Chester;58;179;`m) +/ => name age height sex +/ => ---------------------- +/ => Arthur 35 180 m +/ => Thomas 32 175 m +/ => Polly 52 160 f +/ => John 25 178 m +/ => Chester 58 179 m + +/ 사전이나 테이블도 upsert합니다. +t upsert `name`age`height`sex!(`Chester;58;179;`m) +t upsert (`Chester;58;179;`m) +/ => name age height sex +/ => ---------------------- +/ => Arthur 35 180 m +/ => Thomas 32 175 m +/ => Polly 52 160 f +/ => John 25 178 m +/ => Chester 58 179 m + +/ 그리고 테이블이 키가 지정된 경우 +kt:`name xkey t +/ upsert는 필요한 경우 레코드를 교체합니다. +kt upsert ([]name:`Thomas`Chester;age:33 58;height:175 179;sex:`f`m) +/ => name | age height sex +/ => -------| -------------- +/ => Arthur | 35 180 m +/ => Thomas | 33 175 f +/ => Polly | 52 160 f +/ => John | 25 178 m +/ => Chester| 58 179 m + +/ q-sql에는 ORDER BY 절이 없으며, 대신 xasc/xdesc를 사용합니다. +`name xasc t +/ => name age height sex +/ => --------------------- +/ => Arthur 35 180 m +/ => John 25 178 m +/ => Polly 52 160 f +/ => Thomas 32 175 m + +/ 대부분의 표준 SQL 조인은 q-sql에 있으며, 몇 가지 새로운 친구도 있습니다. +/ http://code.kx.com/q4m3/9_Queries_q-sql/#99-joins 참조 +/ 가장 중요하고(일반적으로 사용되는) 두 가지는 lj와 aj입니다. + +/ lj는 기본적으로 SQL LEFT JOIN과 동일합니다. +/ 조인은 왼쪽 테이블의 키 열에서 수행됩니다. +le:([sex:`m`f]lifeexpectancy:78 85) +t lj le +/ => name age height sex lifeexpectancy +/ => ------------------------------------ +/ => Arthur 35 180 m 78 +/ => Thomas 32 175 m 78 +/ => Polly 52 160 f 85 +/ => John 25 178 m 78 + +/ aj는 asof 조인입니다. 이것은 표준 SQL 조인이 아니며 매우 강력할 수 있습니다. +/ 정식 예는 금융 거래 및 시세 테이블을 조인하는 것입니다. +trades:([]time:10:01:01 10:01:03 10:01:04;sym:`msft`ibm`ge;qty:100 200 150) +quotes:([]time:10:01:00 10:01:01 10:01:01 10:01:03; + sym:`ibm`msft`msft`ibm; px:100 99 101 98) +aj[`time`sym;trades;quotes] +/ => time sym qty px +/ => --------------------- +/ => 10:01:01 msft 100 101 +/ => 10:01:03 ibm 200 98 +/ => 10:01:04 ge 150 +/ 거래 테이블의 각 행에 대해 해당 sym에 대한 마지막(우세한) 시세(px)가 조인됩니다. +/ http://code.kx.com/q4m3/9_Queries_q-sql/#998-as-of-joins 참조 + +//////////////////////////////////// +///// 추가/고급 ////// +//////////////////////////////////// + +////// 부사 ////// +/ 지금까지 루프가 전혀 없다는 것을 눈치채셨을 것입니다. +/ 이것은 실수가 아닙니다! +/ q는 벡터 언어이므로 명시적인 루프(for, while 등)는 권장되지 않습니다. +/ 가능한 경우 기능은 벡터화되어야 합니다(즉, 목록에 대한 연산). +/ 부사는 이를 보완하고, 필요할 때 루프 유형 기능을 제공하면서 함수의 동작을 수정합니다. +/ (q에서 함수는 때때로 동사라고 하므로 부사입니다) +/ "each" 부사는 함수를 수정하여 목록을 개별 변수로 처리합니다. +first each (1 2 3;4 5 6;7 8 9) +/ => 1 4 7 + +/ each-left (\:) 및 each-right (/:)는 두 인수 함수를 수정하여 +/ 인수 중 하나를 목록 대신 개별 변수로 처리합니다. +1 2 3 +\: 11 22 33 +/ => 12 23 34 +/ => 13 24 35 +/ => 14 25 36 +1 2 3 +/: 11 22 33 +/ => 12 13 14 +/ => 23 24 25 +/ => 34 35 36 + +/ q의 루프에 대한 진정한 대안은 부사 scan (\) 및 over (/)입니다. +/ 동작은 수정하는 함수의 인수 수에 따라 다릅니다. 여기서는 가장 유용한 몇 가지 경우를 요약하겠습니다. +/ scan으로 수정된 단일 인수 함수는 "do"와 같이 작동합니다. +{x * 2}\[5;1] / => 1 2 4 8 16 32 (즉, 2를 5번 곱함) +{x * 2}/[5;1] / => 32 (over를 사용하면 최종 결과만 표시됨) + +/ 첫 번째 인수가 함수인 경우 "while"과 동일합니다. +{x * 2}\[{x<100};1] / => 1 2 4 8 16 32 64 128 (0b를 반환할 때까지 반복) +{x * 2}/[{x<100};1] / => 128 (다시 최종 결과만 반환) + +/ 함수가 두 개의 인수를 사용하고 목록을 전달하면 "for"가 있습니다. +/ 이전 실행 결과가 목록의 다음 멤버와 함께 다음 루프로 다시 전달됩니다. +{x + y}\[1 2 3 4 5] / => 1 3 6 10 15 (즉, 누적 합계) +{x + y}/[1 2 3 4 5] / => 15 (최종 결과만) + +/ 다른 반복기 및 용도가 있으며, 이것은 빠른 개요일 뿐입니다. +/ http://code.kx.com/q4m3/6_Functions/#67-iterators + +////// 스크립트 ////// +/ q 스크립트는 "\l" 명령을 사용하여 q 세션에서 로드할 수 있습니다. +/ 예를 들어 "\l learnkdb.q"는 이 스크립트를 로드합니다. +/ 또는 스크립트를 인수로 전달하는 명령 프롬프트에서 +/ 예를 들어 "q learnkdb.q" + +////// 디스크 데이터 ////// +/ 테이블은 여러 형식으로 디스크에 유지될 수 있습니다. +/ 가장 기본적인 두 가지는 직렬화 및 스플레이입니다. +t:([]a:1 2 3;b:1 2 3f) +`:serialized set t / 테이블을 단일 직렬화된 파일로 저장 +`:splayed/ set t / 테이블을 디렉토리에 스플레이하여 저장 + +/ 디렉토리 구조는 다음과 같습니다: +/ db/ +/ ├── serialized +/ └── splayed +/ ├── a +/ └── b + +/ 이 디렉토리를 로드하면(스크립트처럼, 위 참조) +/ 이러한 테이블을 q 세션에 로드합니다. +\l . +/ 직렬화된 테이블은 메모리에 로드됩니다. +/ 그러나 스플레이된 테이블은 매핑만 되고 로드되지는 않습니다. +/ 두 테이블 모두 q-sql을 사용하여 쿼리할 수 있습니다. +select from serialized +/ => a b +/ => --- +/ => 1 1 +/ => 2 2 +/ => 3 3 +select from splayed / (열은 요청 시 디스크에서 읽음) +/ => a b +/ => --- +/ => 1 1 +/ => 2 2 +/ => 3 3 +/ 자세한 내용은 http://code.kx.com/q4m3/14_Introduction_to_Kdb+/를 참조하십시오. + +////// 프레임워크 ////// +/ kdb+는 일반적으로 데이터 캡처 및 분석에 사용됩니다. +/ 여기에는 여러 프로세스가 함께 작동하는 아키텍처를 사용하는 것이 포함됩니다. +/ kdb+ 프레임워크는 이 아키텍처의 설정 및 구성을 간소화하고 재해 복구, 로깅, 액세스, 로드 밸런싱 등과 같은 추가 기능을 추가하는 데 사용할 수 있습니다. +/ https://github.com/DataIntellectTech/TorQ + + +## 더 알고 싶으십니까? + +* [*q for mortals* q 언어 튜토리얼](http://code.kx.com/q4m3/) +* [*Introduction to Kdb+* 디스크 데이터 튜토리얼](http://code.kx.com/q4m3/14_Introduction_to_Kdb+/) +* [q 언어 참조](https://code.kx.com/q/ref/) +* [TorQ 프로덕션 프레임워크](https://github.com/DataIntellectTech/TorQ) \ No newline at end of file diff --git a/ko/lambda-calculus.md b/ko/lambda-calculus.md new file mode 100644 index 0000000000..2bcaa8f8a7 --- /dev/null +++ b/ko/lambda-calculus.md @@ -0,0 +1,199 @@ +--- +category: Algorithms & Data Structures +name: Lambda Calculus +contributors: + - ["Max Sun", "http://github.com/maxsun"] + - ["Yan Hui Hang", "http://github.com/yanhh0"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +# 람다 대수 + +람다 대수(λ-대수)는 원래 [Alonzo Church](https://en.wikipedia.org/wiki/Alonzo_Church)가 만든 세계에서 가장 작은 프로그래밍 언어입니다. 숫자, 문자열, 부울 또는 함수가 아닌 데이터 유형이 없음에도 불구하고 람다 대수는 모든 튜링 기계를 나타내는 데 사용할 수 있습니다! + +람다 대수는 **변수**, **함수** 및 **적용**의 3가지 요소로 구성됩니다. + + +| 이름 | 구문 | 예시 | 설명 | +|-------------|------------------------------------|-----------|-----------------------------------------------| +| 변수 | `` | `x` | "x"라는 이름의 변수 | +| 함수 | `λ.` | `λx.x` | 매개변수 "x"와 본문 "x"가 있는 함수 | +| 적용 | `` | `(λx.x)a` | 인수 "a"로 함수 "λx.x" 호출 | + +가장 기본적인 함수는 `f(x) = x`와 동일한 항등 함수 `λx.x`입니다. 첫 번째 "x"는 함수의 인수이고 두 번째는 함수의 본문입니다. + +## 자유 변수 대 바인딩된 변수: + +- 함수 `λx.x`에서 "x"는 함수의 본문과 매개변수 모두에 있으므로 바인딩된 변수라고 합니다. +- `λx.y`에서 "y"는 이전에 선언되지 않았으므로 자유 변수라고 합니다. + +## 평가: + +평가는 본질적으로 어휘적으로 범위가 지정된 대체인 [β-축소](https://en.wikipedia.org/wiki/Lambda_calculus#Beta_reduction)를 통해 수행됩니다. + +표현식 `(λx.x)a`를 평가할 때 함수의 본문에서 "x"의 모든 발생을 "a"로 바꿉니다. + +- `(λx.x)a`는 `a`로 평가됩니다. +- `(λx.y)a`는 `y`로 평가됩니다. + +고차 함수를 만들 수도 있습니다: + +- `(λx.(λy.x))a`는 `λy.a`로 평가됩니다. + +전통적으로 람다 대수는 단일 매개변수 함수만 지원하지만 [커링](https://en.wikipedia.org/wiki/Currying)이라는 기술을 사용하여 다중 매개변수 함수를 만들 수 있습니다. + +- `(λx.λy.λz.xyz)`는 `f(x, y, z) = ((x y) z)`와 동일합니다. + +때로는 `λxy.`가 `λx.λy.`와 상호 교환적으로 사용됩니다. + +---- + +전통적인 **람다 대수에는 숫자, 문자 또는 함수가 아닌 데이터 유형이 없습니다!** + +## 부울 논리: + +람다 대수에는 "True" 또는 "False"가 없습니다. 1이나 0도 없습니다. + +대신: + +`T`는 `λx.λy.x`로 표시됩니다. + +`F`는 `λx.λy.y`로 표시됩니다. + +먼저, `b`가 True이면 `t`를 반환하고 `b`가 False이면 `f`를 반환하는 "if" 함수 `λbtf`를 정의할 수 있습니다. + +`IF`는 `λb.λt.λf.b t f`와 동일합니다. + +`IF`를 사용하여 기본 부울 논리 연산자를 정의할 수 있습니다: + +`a AND b`는 `λab.IF a b F`와 동일합니다. + +`a OR b`는 `λab.IF a T b`와 동일합니다. + +`NOT a`는 `λa.IF a F T`와 동일합니다. + +*참고: `IF a b c`는 본질적으로 `IF((a b) c)`를 의미합니다.* + +## 숫자: + +람다 대수에는 숫자가 없지만 [처치 숫자](https://en.wikipedia.org/wiki/Church_encoding)를 사용하여 숫자를 인코딩할 수 있습니다. + +모든 숫자 n에 대해: n = λf.fn이므로: + +`0 = λf.λx.x` + +`1 = λf.λx.f x` + +`2 = λf.λx.f(f x)` + +`3 = λf.λx.f(f(f x))` + +처치 숫자를 증가시키려면 다음인 후계자 함수 `S(n) = n + 1`을 사용합니다: + +`S = λn.λf.λx.f((n f) x)` + +후계자를 사용하여 덧셈을 정의할 수 있습니다: + +`ADD = λab.(a S)b` + +**도전:** 자신만의 곱셈 함수를 정의해 보십시오! + +## 더 작게 만들기: SKI, SK 및 Iota + +### SKI 조합자 미적분학 + +S, K, I를 다음 함수라고 합시다: + +`I x = x` + +`K x y = x` + +`S x y z = x z (y z)` + +람다 대수의 표현식을 SKI 조합자 미적분학의 표현식으로 변환할 수 있습니다: + +1. `λx.x = I` +2. `x`가 `c`에서 자유롭게 발생하지 않는 경우 `λx.c = Kc` +3. `λx.(y z) = S (λx.y) (λx.z)` + +예를 들어 처치 숫자 2를 들어보겠습니다: + +`2 = λf.λx.f(f x)` + +내부 부분 `λx.f(f x)`의 경우: + +``` + λx.f(f x) += S (λx.f) (λx.(f x)) (사례 3) += S (K f) (S (λx.f) (λx.x)) (사례 2, 3) += S (K f) (S (K f) I) (사례 2, 1) +``` + +따라서: + +``` + 2 += λf.λx.f(f x) += λf.(S (K f) (S (K f) I)) += λf.((S (K f)) (S (K f) I)) += S (λf.(S (K f))) (λf.(S (K f) I)) (사례 3) +``` + +첫 번째 인수 `λf.(S (K f))`의 경우: + +``` + λf.(S (K f)) += S (λf.S) (λf.(K f)) (사례 3) += S (K S) (S (λf.K) (λf.f)) (사례 2, 3) += S (K S) (S (K K) I) (사례 2, 3) +``` + +두 번째 인수 `λf.(S (K f) I)`의 경우: + +``` + λf.(S (K f) I) += λf.((S (K f)) I) += S (λf.(S (K f))) (λf.I) (사례 3) += S (S (λf.S) (λf.(K f))) (K I) (사례 2, 3) += S (S (K S) (S (λf.K) (λf.f))) (K I) (사례 1, 3) += S (S (K S) (S (K K) I)) (K I) (사례 1, 2) +``` + +병합: + +``` + 2 += S (λf.(S (K f))) (λf.(S (K f) I)) += S (S (K S) (S (K K) I)) (S (S (K S) (S (K K) I)) (K I)) +``` + +이것을 확장하면 처치 숫자 2에 대한 동일한 표현식을 다시 얻게 됩니다. + +### SK 조합자 미적분학 + +SKI 조합자 미적분학은 여전히 더 줄일 수 있습니다. `I = SKK`라는 점에 유의하여 I 조합자를 제거할 수 있습니다. 모든 `I`를 `SKK`로 대체할 수 있습니다. + +### Iota 조합자 + +SK 조합자 미적분학은 여전히 최소가 아닙니다. 정의: + +``` +ι = λf.((f S) K) +``` + +우리는 다음을 가지고 있습니다: + +``` +I = ιι +K = ι(ιI) = ι(ι(ιι)) +S = ι(K) = ι(ι(ι(ιι))) +``` + +## 더 고급 읽기: + +1. [람다 대수 튜토리얼 소개](http://www.inf.fu-berlin.de/lehre/WS03/alpi/lambda.pdf) +2. [Cornell CS 312 Recitation 26: 람다 대수](http://www.cs.cornell.edu/courses/cs3110/2008fa/recitations/rec26.html) +3. [위키백과 - 람다 대수](https://en.wikipedia.org/wiki/Lambda_calculus) +4. [위키백과 - SKI 조합자 미적분학](https://en.wikipedia.org/wiki/SKI_combinator_calculus) +5. [위키백과 - Iota 및 Jot](https://en.wikipedia.org/wiki/Iota_and_Jot) \ No newline at end of file diff --git a/ko/latex.md b/ko/latex.md new file mode 100644 index 0000000000..cf0a7f22a1 --- /dev/null +++ b/ko/latex.md @@ -0,0 +1,284 @@ +--- +name: LaTeX +contributors: + - ["Chaitanya Krishna Ande", "http://icymist.github.io"] + - ["Colton Kohnke", "https://github.com/voltnor"] + - ["Sricharan Chiruvolu", "http://sricharan.xyz"] + - ["Ramanan Balakrishnan", "https://github.com/ramananbalakrishnan"] + - ["Svetlana Golubeva", "https://attillax.github.io/"] + - ["Oliver Kopp", "http://orcid.org/0000-0001-6962-4290"] +filename: learn-latex.tex +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```tex +% 모든 주석 줄은 %로 시작합니다. +% 여러 줄 주석은 없습니다. + +% LaTeX는 MS Word나 OpenOffice Writer와 같은 "보이는 대로 얻는" 워드 프로세싱 소프트웨어가 아닙니다. + +% 모든 LaTeX 명령은 백슬래시(\)로 시작합니다. + +% LaTeX 문서는 컴파일할 문서 유형을 정의하는 것으로 시작합니다. +% 다른 문서 유형에는 책, 보고서, 프레젠테이션 등이 포함됩니다. +% 문서에 대한 옵션은 [] 대괄호 안에 나타납니다. 이 경우 12pt 글꼴을 사용하도록 지정합니다. +\documentclass[12pt]{article} + +% 다음으로 문서가 사용하는 패키지를 정의합니다. +% 다른 언어 파일의 그래픽, 색상 텍스트 또는 소스 코드를 문서에 포함하려면 LaTeX의 기능을 향상시켜야 합니다. 이것은 패키지를 추가하여 수행됩니다. +% 그림을 위해 float 및 caption 패키지를 포함하고 하이퍼링크를 위해 hyperref 패키지를 포함합니다. +\usepackage{caption} +\usepackage{float} +\usepackage{hyperref} + +% 다른 문서 속성도 정의할 수 있습니다! +\author{Chaitanya Krishna Ande, Colton Kohnke, Sricharan Chiruvolu & \\ +Svetlana Golubeva} +\date{\today} +\title{Y분 만에 \LaTeX{} 배우기!} + +% 이제 문서를 시작할 준비가 되었습니다. +% 이 줄 이전의 모든 것을 "서문"이라고 합니다. +\begin{document} +% 저자, 날짜, 제목 필드를 설정하면 LaTeX가 제목 페이지를 만들어 줄 수 있습니다. +\maketitle + +% 섹션이 있는 경우 목차를 만들 수 있습니다. 올바른 순서로 표시되도록 문서를 두 번 컴파일해야 합니다. +% 문서 본문에서 목차를 분리하는 것이 좋습니다. 이를 위해 \newpage 명령을 사용합니다. +\newpage +\tableofcontents + +\newpage + +% 대부분의 연구 논문에는 초록이 있으며, 이를 위해 미리 정의된 명령을 사용할 수 있습니다. +% 이것은 논리적 순서대로 나타나야 하므로, 상단 자료 뒤, 본문의 주요 섹션 앞에 나타나야 합니다. +% 이 명령은 문서 클래스 article 및 report에서 사용할 수 있습니다. +\begin{abstract} + \LaTeX{} 문서가 \LaTeX{}로 작성되었습니다! 얼마나 참신하고 완전히 제 아이디어가 아닌지! +\end{abstract} + +% 섹션 명령은 직관적입니다. +% 모든 섹션 제목은 목차에 자동으로 추가됩니다. +\section{소개} +안녕하세요, 제 이름은 Colton이고 함께 \LaTeX{}를 탐색할 것입니다! + +\section{다른 섹션} +이것은 다른 섹션의 텍스트입니다. 하위 섹션이 필요하다고 생각합니다. + +\subsection{이것은 하위 섹션입니다} % 하위 섹션도 직관적입니다. +또 다른 것이 필요하다고 생각합니다. + +\subsubsection{피타고라스} +이제 훨씬 낫습니다. +\label{subsec:pythagoras} + +% 별표를 사용하면 LaTeX의 내장 번호 매기기를 억제할 수 있습니다. +% 이것은 다른 LaTeX 명령에서도 작동합니다. +\section*{이것은 번호가 매겨지지 않은 섹션입니다} +그러나 모든 섹션에 번호가 매겨져야 하는 것은 아닙니다! + +\section{일부 텍스트 참고 사항} +%\section{간격} % 공간 간격에 대한 더 많은 정보를 추가해야 합니다. +\LaTeX{}는 일반적으로 텍스트를 있어야 할 곳에 배치하는 데 매우 능숙합니다. +줄이 \\ 끊어져야 하는 경우 \\ 소스 코드에 추가합니다. + +빈 줄로 단락을 구분합니다. + +약어 뒤에 쉼표가 없는 경우 비분리 공백을 위해 약어 뒤에 물결표를 추가해야 합니다. 그렇지 않으면 점 뒤의 간격이 너무 큽니다: +예:, 즉, 등~은 그러한 약어입니다. + +\section{목록} +목록은 \LaTeX{}에서 가장 쉽게 만들 수 있는 것 중 하나입니다! 내일 쇼핑을 가야 하므로 식료품 목록을 만들어 보겠습니다. +\begin{enumerate} % 이것은 "enumerate" 환경을 만듭니다. + % \item은 enumerate에 증가하도록 지시합니다. + \item 샐러드. + \item 수박 27개. + \item 잭래빗 한 마리. + % []를 사용하여 항목 번호를 재정의할 수도 있습니다. + \item[몇 개?] 중간 크기 물총. + + 목록 항목은 아니지만 여전히 enumerate의 일부입니다. + +\end{enumerate} % 모든 환경에는 끝이 있어야 합니다. + +\section{수학} + +\LaTeX{}의 주요 용도 중 하나는 학술 논문이나 기술 문서를 작성하는 것입니다. 일반적으로 수학 및 과학 분야입니다. 따라서 논문에 특수 기호를 추가할 수 있어야 합니다! + +수학에는 키보드에서 찾을 수 있는 것보다 훨씬 많은 기호가 있습니다. +집합 및 관계 기호, 화살표, 연산자 및 그리스 문자가 그 예입니다. + +집합과 관계는 많은 수학 연구 논문에서 중요한 역할을 합니다. X에 속하는 모든 x를 다음과 같이 나타냅니다: $\forall x \in X$. +% 기호 앞뒤에 $ 기호를 추가해야 했습니다. 이것은 작성할 때 텍스트 모드에 있기 때문입니다. +% 그러나 수학 기호는 수학 모드에만 존재합니다. +% $ 기호로 텍스트 모드에서 수학 모드로 들어갈 수 있습니다. +% 반대도 마찬가지입니다. 변수도 수학 모드에서 렌더링할 수 있습니다. +% \[\ \]로 수학 모드로 들어갈 수도 있습니다. + +\[a^2 + b^2 = c^2 \] + +제가 가장 좋아하는 그리스 문자는 $\xi$입니다. $\beta$, $\gamma$ 및 $\sigma$도 좋아합니다. +아직 \LaTeX{}가 모르는 그리스 문자를 찾지 못했습니다! + +연산자는 수학 문서의 필수적인 부분입니다: 삼각 함수($\sin$, $\cos$, $\tan$), 로그 및 지수($\log$, $\exp$), 극한($\lim$) 등에는 미리 정의된 LaTeX 명령이 있습니다. 어떻게 하는지 보기 위해 방정식을 작성해 보겠습니다: +$\cos(2\theta) = \cos^{2}(\theta) - \sin^{2}(\theta)$ + +분수(분자-분모)는 다음과 같은 형식으로 작성할 수 있습니다: + +% 10 / 7 +$$ ^{10}/_{7} $$ + +% 비교적 복잡한 분수는 다음과 같이 작성할 수 있습니다. +% \frac{분자}{분모} +$$ \frac{n!}{k!(n - k)!} $$ + +방정식을 "방정식 환경"에 삽입할 수도 있습니다. + +% 방정식 '환경'으로 수학 표시 +\begin{equation} % 수학 모드로 들어감 + c^2 = a^2 + b^2. + \label{eq:pythagoras} % 참조용 +\end{equation} % 모든 \begin 문에는 end 문이 있어야 합니다. + +그런 다음 새 방정식을 참조할 수 있습니다! +Eqn.\ref{eq:pythagoras}는 피타고라스 정리라고도 하며, Sec.\ref{subsec:pythagoras}의 주제이기도 합니다. 그림, 방정식, 섹션 등 많은 것을 레이블로 지정할 수 있습니다. + +합계 및 적분은 sum 및 int 명령으로 작성됩니다: + +% 일부 LaTeX 컴파일러는 빈 줄이 있으면 불합니다. +% 방정식 환경에서. +\begin{equation} + \sum_{i=0}^{5} f_{i} +\end{equation} +\begin{equation} + \int_{0}^{\infty} \mathrm{e}^{-x} \mathrm{d}x +\end{equation} + +\section{그림} + +그림을 삽입해 보겠습니다. 그림 배치는 약간 까다로울 수 있습니다. 기본 옵션은 위쪽 [t], 아래쪽 [b], 여기 [h](대략)입니다. +매번 배치 옵션을 찾아봐야 합니다. +% 자세한 내용은 https://en.wikibooks.org/wiki/LaTeX/Floats,_Figures_and_Captions를 참조하십시오. + +\begin{figure}[H] % H는 여기서 배치 옵션을 나타냅니다. + \centering % 페이지에서 그림을 가운데에 맞춥니다. + % 페이지 너비의 0.8로 크기가 조정된 그림을 삽입합니다. + %\includegraphics[width=0.8\linewidth]{right-triangle.png} + % 컴파일 목적으로 주석 처리되었습니다. 상상력을 발휘하십시오. + \caption{변 $a$, $b$, $c$가 있는 직각 삼각형} + \label{fig:right-triangle} +\end{figure} + +\subsection{표} +그림과 동일한 방식으로 표를 삽입할 수도 있습니다. + +\begin{table}[H] + \caption{표에 대한 캡션입니다.} + % 아래의 {} 인수는 테이블의 각 행이 어떻게 그려지는지 설명합니다. + % 기본은 간단합니다: 각 열에 대해 하나의 문자로 정렬을 제어합니다: + % 기본 옵션은 c, l, r 및 p이며, 각각 가운데, 왼쪽, 오른쪽 및 단락입니다. + % 선택적으로 수직선을 위해 |를 추가할 수 있습니다. + % 자세한 내용은 https://en.wikibooks.org/wiki/LaTeX/Tables를 참조하십시오. + \begin{tabular}{c|cc} % 여기서 "가운데 | 수직선, 가운데 가운데"를 의미합니다. + Number & First Name & Last Name \ % 열 행은 &로 구분됩니다. + \hline % 수평선 + 1 & Biggus & Dickus \ + 2 & Monty & Python + \end{tabular} + % 대략 다음과 같이 표시됩니다. + % Number | First Name Last Name + % -------|--------------------------- % \hline 때문에 + % 1 | Biggus Dickus + % 2 | Monty Python +\end{table} + +\section{\LaTeX{}가 무언가를 컴파일하지 않도록 하기 (즉, 소스 코드)} +\LaTeX{} 문서에 일부 코드를 포함하고 싶다고 가정해 보겠습니다. 그러면 \LaTeX{}가 해당 텍스트를 해석하려고 시도하지 않고 대신 문서에 인쇄하도록 해야 합니다. 이것은 verbatim 환경으로 수행합니다. + +% 다른 패키지가 존재하지만(즉, minty, lstlisting 등) verbatim은 기본입니다. +\begin{verbatim} + print("Hello World!") + a%b; % 보세요! verbatim에서 % 기호를 사용할 수 있습니다. + random = 4; #공정한 임의 주사위 굴림으로 결정됨, https://www.xkcd.com/221/ + https://www.explainxkcd.com/wiki/index.php/221:_Random_Number 참조 +\end{verbatim} + +\section{컴파일} + +이제 이 멋진 문서를 컴파일하고 \LaTeX{} pdf의 영광스러운 영광을 보는 방법을 궁금해하실 것입니다. +(예, 이 문서는 실제로 컴파일됩니다). + +\LaTeX{}를 사용하여 최종 문서를 얻는 것은 다음 단계로 구성됩니다: + \begin{enumerate} + \item 일반 텍스트로 문서를 작성합니다("소스 코드"). + \item 소스 코드를 컴파일하여 pdf를 생성합니다. + 컴파일 단계는 다음과 같습니다(Linux에서): \\ + \begin{verbatim} + > pdflatex learn-latex.tex + \end{verbatim} + \end{enumerate} + +많은 \LaTeX{} 편집기는 1단계와 2단계를 동일한 소프트웨어에 결합합니다. 따라서 1단계를 볼 수 있지만 2단계는 완전히 볼 수 없습니다. +2단계는 여전히 백그라운드에서 발생합니다\footnote{참조(예: Eqn.\ref{eq:pythagoras})를 사용하는 경우 중간 *.aux 파일을 생성하기 위해 2단계를 여러 번 실행해야 할 수 있습니다.}. +% 또한 이것이 문서에 각주를 추가하는 방법입니다! +% 간단한 \footnote{...} 명령으로. 기본적으로 ¹, ², ...로 번호가 매겨집니다. + +1단계에서 모든 서식 정보를 일반 텍스트로 작성합니다. +2단계의 컴파일 부분은 1단계에서 정의한 형식으로 문서를 생성하는 것을 처리합니다. + +\section{하이퍼링크} +문서에 하이퍼링크를 삽입할 수도 있습니다. 이를 위해 서문에 hyperref 패키지를 포함해야 합니다: +\begin{verbatim} + \usepackage{hyperref} +\end{verbatim} + +두 가지 주요 유형의 링크가 있습니다: 보이는 URL \\ +\url{https://learnxinyminutes.com/latex/}, 또는 +\href{https://learnxinyminutes.com/latex/}{텍스트로 가려짐} +% 컴파일 중에 실수를 유발하므로 그림자 텍스트에 추가 공백이나 특수 기호를 추가할 수 없습니다. + +이 패키지는 또한 출력 PDF 문서에 썸네일 목록과 목차에 활성 링크를 생성합니다. + +\section{ASCII 또는 다른 인코딩으로 작성} + +기본적으로 역사적으로 LaTeX는 순수 ASCII(128)인 입력을 허용하지만, 악센트(à, è 등) 및 비라틴 기호가 없는 확장 ASCII는 허용하지 않습니다. + +악센트 및 기본 라틴 기호를 삽입하는 것은 백슬래시 바로 가기를 사용하여 쉽습니다. +\,c, \'e, \`A, \ae 및 \oe 등과 같습니다. % ç, é, À 등의 경우 +% 자세한 내용은 https://en.wikibooks.org/wiki/LaTeX/Special_Characters#Escaped_codes를 참조하십시오. + +UTF-8로 직접 작성하려면 pdflatex로 컴파일할 때 다음을 사용하십시오. +\begin{verbatim} + \usepackage[utf8]{inputenc} +\end{verbatim} +선택한 글꼴은 문서에 사용된 글리프를 지원해야 하며, 다음을 추가해야 합니다. +\begin{verbatim} + \usepackage[T1]{fontenc} +\end{verbatim} + +LuaTeX 및 XeLaTeX는 UTF-8을 내장 지원하도록 설계되었으므로 비라틴 알파벳으로 작성하는 것이 더 쉽습니다. + +\section{끝} + +지금은 여기까지입니다! + +% 대부분의 경우 문서에 참조 섹션이 필요합니다. +% 이를 설정하는 가장 쉬운 방법은 참고 문헌 섹션을 사용하는 것입니다. +\begin{thebibliography}{1} + % 다른 목록과 마찬가지로 \bibitem 명령을 사용하여 항목을 나열할 수 있습니다. + % 각 항목은 텍스트 본문에서 직접 인용할 수 있습니다. + \bibitem{latexwiki} 놀라운 \LaTeX{} 위키북: \emph{https://en.wikibooks.org/wiki/LaTeX} + \bibitem{latextutorial} 실제 튜토리얼: \emph{http://www.latex-tutorial.com} +\end{thebibliography} + +% 문서 끝 +\end{document} + +## LaTeX에 대한 추가 정보 + +* 놀라운 LaTeX 위키북: [https://en.wikibooks.org/wiki/LaTeX](https://en.wikibooks.org/wiki/LaTeX) +* 실제 튜토리얼: [http://www.latex-tutorial.com/](http://www.latex-tutorial.com/) +* LaTeX를 배우기 위한 빠른 가이드: [30분 만에 LaTeX 배우기](https://www.overleaf.com/learn/latex/Learn_LaTeX_in_30_minutes) +* LaTeX를 배우기 위한 대화형 플랫폼(설치 불필요) [learnlatex.org/](https://www.learnlatex.org/) +* TeX, LaTeX, ConTeXt 등에 대한 Stack Exchange의 질문 및 답변 사이트 [tex.stackexchange.com](https://tex.stackexchange.com/) \ No newline at end of file diff --git a/ko/lbstanza.md b/ko/lbstanza.md new file mode 100644 index 0000000000..ca181a51dd --- /dev/null +++ b/ko/lbstanza.md @@ -0,0 +1,285 @@ +--- +name: LB Stanza +filename: learn-stanza.stanza +contributors: + - ["Mike Hilgendorf", "https://github.com/m-hilgendorf"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +LB Stanza(또는 줄여서 Stanza)는 캘리포니아 대학교 버클리에서 만든 새로운 선택적 타입 범용 프로그래밍 언어입니다. Stanza는 프로그래머가 대규모 프로그램 아키텍처의 복잡성을 해결하고 전체 소프트웨어 개발 수명 주기 동안 애플리케이션 프로그래머의 생산성을 크게 향상시키는 데 도움이 되도록 설계되었습니다. + + +``` +; 이것은 주석입니다. +; +이것은 블록 주석입니다. + ; + 블록 주석은 선택적 태그로 중첩될 수 있습니다. + ; +; +defpackage learn-stanza-in-y: + import core + import collections + +;============================================================================== +; 대부분의 프로그래밍 언어에서 볼 수 있는 기본 사항 +;============================================================================== + + +; 변수는 가변(var) 또는 불변(val)일 수 있습니다. +val immutable = "this string can't be changed" +var mutable = "this one can be" +mutable = "like this" + +; 기본 데이터 타입 (주석은 선택 사항) +val an-int: Int = 12345 +val a-long: Long = 12345L +val a-float: Float = 1.2345f +val a-double: Double = 3.14159 +val a-string: String = "this is a string" +val a-multiline-string = \ + this is a "raw" string literal +\ + +; println과 "..." % [...]를 사용하여 서식화된 문자열 출력 +println("this is a formatted string %_ %_" % [mutable, immutable]) + +; Stanza는 선택적 타입이며, ? (any) 타입을 가집니다. +var anything:? = 0 +anything = 3.14159 +anything = "a string" + +; Stanza에는 튜플, 배열, 벡터 및 해시 테이블과 같은 기본 컬렉션이 있습니다. +val tuple: Tuple = [mutable, immutable] + +val array = Array(3) +array[0] = "string" +array[1] = 1 +array[2] = 1.23455 +; array[3] = "out-of-bounds" ; 배열은 경계 검사를 합니다. + +val vector = Vector() +vector[0] = "string" +vector[1] = 1 +vector[2] = 3.14159 + +val hash-table = HashTable() +hash-table["0"] = 0 +hash-table["1"] = 1 +hash-table["2"] = 1 + + +;============================================================================== +; 함수 +;============================================================================== +; 함수는 `defn` 키워드로 선언됩니다. +defn my-function (arg:?) : ; 식별자와 인수 목록 사이에 공백이 있습니다. + println("called my-function with %_" % [arg]) + +my-function("arg") ; 함수를 호출할 때 공백이 없습니다. + +; 함수는 다른 함수 내부에 선언될 수 있으며, +; 주변 환경의 변수를 캡처할 수 있습니다. +defn outer (arg): + defn inner (): + println("outer had arg: %_" % [arg]) + inner() + +outer("something") + +; 함수는 stanza에서 "일급"입니다. 즉, 변수를 +; 함수에 할당하고 함수를 다른 함수에 인수로 전달할 수 있습니다. +val a-function = outer +defn do-n-times (arg, func, n:Int): + for i in 0 to n do : + func(arg) +do-n-times("argument", a-function, 3) + +; 때로는 함수를 인라인으로 정의하거나 익명 함수를 사용하고 싶을 때가 있습니다. +; 이를 위해 다음 구문을 사용할 수 있습니다. +; fn (args): +; ... +do-n-times("hello", fn (arg): println(arg), 2) + +; 익명 함수를 작성하는 약식 구문이 있습니다. +do-n-times("hello", { println(_) }, 2) + +; 약식 구문은 여러 인수에도 작동합니다. +val multi-lambda = { println(_ + 2 * _) } +multi-lambda(1, 2) + +;============================================================================== +; 사용자 정의 타입 +;============================================================================== +; 구조체는 `defstruct` 키워드로 선언됩니다. +defstruct MyStruct: + field + +; 생성자는 자동으로 파생됩니다. +val my-struct = MyStruct("field:value") + +; 필드는 함수 호출 구문을 사용하여 액세스됩니다. +println(field(my-struct)) + +; Stanza는 메서드 오버로딩을 기반으로 한 "다중 메서드" 시스템으로 +; 서브타이핑을 지원합니다. +deftype MyType +defmulti a-method (m:MyType) + +defstruct Foo <: MyType +defstruct Bar <: MyType +defmethod a-method (a-foo: Foo): + println("called a-method on a Foo") + +defmethod a-method (a-foo: Bar): + println("called a-method on a Bar") + +;============================================================================== +; 타입 시스템 +;============================================================================== +; True와 False는 단일 값을 가진 타입입니다. +val a-true: True = true +val a-false: False = false + +; 유니온 타입을 선언할 수 있습니다. 즉, 여러 타입 중 하나의 값을 가질 수 있습니다. +val a-boolean: True|False = true +val another-boolean: True|False = false + +; 타입에 대해 패턴 매칭을 할 수 있습니다. +match(a-boolean): + (t:True): println("is true") + (f:False): println("is false") + +; 단일 가능한 타입에 대해 매칭할 수 있습니다. +match(a-boolean:True): + println("is still true") +else: + println("is not true") + +; 변수의 타입을 중심으로 프로그램 로직을 구성할 수 있습니다. +if anything is Float : + println("anything is a float") +else if anything is-not String : + println("anything is not an int") +else : + println("I don't know what anything is") + +;============================================================================== +; 제어 흐름 +;============================================================================== +; stanza에는 표준 기본 제어 흐름이 있습니다. +val condition = [false, false] +if condition[0] : + ; 무언가 수행 + false +else if condition[1] : + ; 다른 작업 수행 + false +else : + ; 그 외 모든 것 + false + +; 값에 대해 패턴 매칭하는 데 사용할 수 있는 switch 문도 있습니다. +; (타입과 반대) +switch(anything): + "this": false + "that": false + "the-other-thing": false + else: false + +; for 및 while 루프가 지원됩니다. +while condition[0]: + println("do stuff") + +for i in 0 to 10 do: + vector[i] = i + +; stanza는 break 또는 return 문으로 작동할 수 있는 +; 명명된 레이블도 지원합니다. +defn another-fn (): + label return: + label break: + while true: + if condition[0] is False: + break(false) + return(false) + +; Stanza의 고급 제어 흐름에 대한 포괄적인 가이드는 +; Stanza-by-Example의 이 페이지를 확인하십시오: http://lbstanza.org/chapter9.html + +;============================================================================== +; 시퀀스 +;============================================================================== +; "for" 루프는 더 강력한 구문의 설탕입니다. +val xs = [1, 2, 3] +val ys = ['a', 'b', 'c'] +val zs = ["foo", "bar", "baz"] + +for (x in xs, y in ys, z in zs) do : + println("x:%_, y:%_, z:%_" % [x, y, z]) + + +;xs, ys, zs는 모두 "Seqable"입니다. 즉, Seq 타입(시퀀스)입니다. +; `do` 식별자는 for 루프의 본문을 시퀀스의 각 요소에 +; 적용하는 특수 함수입니다. +; +; 일반적인 시퀀스 작업은 시퀀스를 연결하는 것입니다. 이것은 +; `seq-cat` 함수를 사용하여 수행됩니다. 이것은 반복자를 "평탄화"하는 것과 +; 유사합니다. +val concat = to-tuple $ + for sequence in [xs, ys, zs] seq-cat: + sequence + +; 여러 시퀀스의 요소를 인터리브하는 변형을 사용할 수도 있습니다. +val interleaved = to-tuple $ + for (x in xs, y in ys, z in zs) seq-cat : + [x, y, z] + +println("[%,] [%,]" % [concat, interleaved]) + +; 또 다른 일반적인 작업은 시퀀스를 다른 시퀀스에 매핑하는 것입니다. 예를 들어 +; 숫자 목록의 모든 요소를 상수로 곱하는 것입니다. 이를 위해 `seq`를 사용합니다. +var numbers = [1.0, 2.0, 3.0, 4.0] +numbers = to-tuple $ + for n in numbers seq : + 2.0 * n +println("%," % [numbers]) + +if find({_ == 2.0}, numbers) is-not False : + println("found it!") + +; 또는 시퀀스에 무언가 있는지 알고 싶을 수도 있습니다. +var is-there = + for n in numbers any? : + n == 2.0 + +; 이것은 "구문 설탕"이므로 +; 익명 함수를 사용하여 명시적으로 작성할 수 있습니다. +is-there = any?({_ == 2.0}, numbers) + +; 시퀀스 라이브러리 및 다양한 어댑터에 대한 자세한 참조는 +; 여기에서 찾을 수 있습니다: http://lbstanza.org/reference.html#anchor439 + + +========================================================================= +; 문서 +;========================================================================= +; +; 최상위 문에는 문자열 값을 사용하는 "doc" 필드가 접두사로 붙을 수 있으며, +; 패키지에 대한 문서를 자동으로 생성하는 데 사용됩니다. +doc: \ + # 문서 문자열 + + ``` + val you-can = "include code snippets, too" + ``` + + 문서를 마크다운(mdbook과 호환)으로 렌더링하려면 + + ```bash + stanza doc source.stanza -o docs + ``` +\ +defn docfn () : false +``` diff --git a/ko/ldpl.md b/ko/ldpl.md new file mode 100644 index 0000000000..6e3164bd78 --- /dev/null +++ b/ko/ldpl.md @@ -0,0 +1,172 @@ +--- +name: LDPL +filename: learnLDPL.ldpl +contributors: + - ["Martín del Río", "https://github.com/lartu"] + - ["John Paul Wohlscheid", "https://github.com/JohnBlood"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +**LDPL**은 C++로 트랜스파일되는 강력한 오픈 소스 프로그래밍 언어로, +처음부터 과도하게 표현력이 풍부하고, 읽기 쉽고, 빠르고, 배우기 쉽도록 설계되었습니다. +COBOL과 같은 오래된 프로그래밍 언어와 유사하게 평이한 영어를 모방하여 +누구나 이해할 수 있기를 바라는 마음으로 만들어졌습니다. 매우 이식성이 뛰어나고 +다양한 아키텍처와 운영 체제에서 실행되며, UTF-8을 +즉시 지원합니다. + +[여기에서 더 읽어보세요.](https://github.com/lartu/ldpl) + +```coffeescript +# 이것은 LDPL의 한 줄 주석입니다. +# LDPL에는 여러 줄 주석이 없습니다. + +# LDPL은 대소문자를 구분하지 않는 언어입니다: dIsPlaY와 DISPLAY는 동일한 +# 문장이며, foo와 FOO는 동일한 변수를 명명합니다. + +# LDPL 소스 파일은 DATA 섹션과 PROCEDURE 섹션의 두 섹션으로 나뉩니다. + +DATA: +# DATA 섹션 내에서 변수가 선언됩니다. + +myNumber is number # 실수를 정의합니다. +myString is text # 문자열을 정의합니다. +myList is number list # 숫자 목록을 정의합니다. +myMap is number map # 숫자 맵을 정의합니다. + +# LDPL은 네 가지 데이터 타입을 이해합니다: 두 가지 스칼라 타입 (NUMBER, TEXT) +# 및 두 가지 컨테이너 타입 (LISTs 및 MAPs). +# LISTs는 TEXT LISTs 또는 NUMBER LISTs가 될 수 있으며, MAPs는 +# TEXT MAPs 및 NUMBER MAPs가 될 수 있습니다. 더 큰 데이터 타입을 만들기 위해 +# 많은 컨테이너를 연결할 수도 있습니다. +textListList is text list list +myMulticontainer is number list list map +# 숫자 목록의 목록의 목록의 맵을 정의합니다. + +PROCEDURE: +# PROCEDURE 섹션 내에서 코드가 작성됩니다. + +store -19.2 in myNumber # 값을 할당하려면 STORE 문을 사용합니다. +store "Hi there" in myString # 변수에. +push 890 to myList # 목록에 값을 추가하려면 PUSH - TO를 사용합니다. +push 100 to myList +push 500 to myList +store 45 in myMap:"someIndex" # 컨테이너를 인덱싱하려면 : 연산자를 사용합니다. + +push list to textListList # 목록 목록에 빈 목록을 푸시합니다. +push "LDPL is nice!" to textListList:0 #푸시된 목록에 텍스트를 푸시합니다. + +display "Hello World!" # 값을 출력하려면 DISPLAY 문을 사용합니다. +# display 문은 공백으로 구분된 여러 값을 받을 수 있습니다. +display crlf "How are you today?" myNumber myString crlf +# CRLF는 LDPL의 표준 줄 바꿈 값입니다. +display textListList:0:0 " Isn't it?" crlf + +# LDPL의 IF 문은 매우 장황합니다: +if myNumber is equal to -19.2 and myList:0 is less than 900 then + display "Yes!" crlf +else if myMap:"someIndex" is not equal to 45 then + display "This is an else if!" crlf +else + display "Else!" crlf +end if +# 유효한 LDPL 비교 연산자는 다음과 같습니다. +# - IS EQUAL TO +# - IS NOT EQUAL TO +# - IS LESS THAN +# - IS GREATER THAN +# - IS LESS THAN OR EQUAL TO +# - IS GREATER THAN OR EQUAL TO +if "Hi there!" is not equal to "Bye bye!" then + display "Yep, those weren't equal." crlf +end if +# LDPL은 일반적으로 인라인 표현식을 이해하지 못하므로 +# 다음과 같은 작업을 수행할 수 없습니다. +# if myNumber - 9 * 2 is equal to 10 then +# 그렇게 하면 LDPL이 컴퓨터에 불을 지르고 화면을 터뜨릴 것입니다. + +# WHILE 루프는 동일한 규칙을 따릅니다. +store 0 in myNumber +while myNumber is less than 10 do + display "Loop number " myNumber "..." crlf + in myNumber solve myNumber + 1 # 이렇게 수학을 할 수 있습니다. +repeat +# 다른 언어와 마찬가지로 루프 내에서 'break' 및 'continue'를 사용할 수 있습니다. + +# LDPL에는 FOR 루프와 FOR EACH 루프도 있습니다. +for myNumber from 0 to 100 step 2 do + display myNumber crlf +repeat + +for each myNumber in myList do + display myNumber +repeat + +display "Enter your name: " +accept myString # 사용자가 값을 입력하도록 하려면 ACCEPT를 사용합니다. +display "Hi there, " myString crlf +display "How old are you?: " +accept myNumber +if myNumber is greater than 200 then + display "Woah, you are so old!" crlf +end if + +wait 1000 milliseconds # 프로그램을 1초 동안 일시 중지합니다. + +# 수학을 좀 해보자 +store 1.2 in myNumber +in myNumber solve myNumber * (10 / 7.2) # 연산자는 공백으로 구분됩니다. +floor myNumber +display myNumber crlf +get random in myNumber # 0과 1 사이의 난수를 얻습니다. + # 그리고 myNumber에 저장합니다. + +# LDPL의 함수는 서브 프로시저라고 합니다. 서브 프로시저는 소스 +# 파일과 마찬가지로 섹션으로 나뉩니다. 서브 프로시저에서 발견되는 섹션은 +# PARAMETERS 섹션, LOCAL DATA 섹션 및 PROCEDURE 섹션입니다. +# PROCEDURE 섹션을 제외한 모든 섹션은 사용하지 않는 경우 건너뛸 수 있습니다. +# PARAMETERS 또는 LOCAL DATA 섹션이 사용되지 않는 경우 PROCEDURE +# 키워드를 생략할 수 있습니다. +sub myFunction + parameters: + a is number # LDPL은 참조에 의한 전달입니다. + b is number + result is number # 따라서 매개변수를 통해 값을 반환할 수 있습니다. + local data: + c is number + procedure: + get random in c + in result solve a + b * c +end sub + +sub sayHello + display "Hi there!" crlf + return + display "This won't be displayed :(" +end sub + +call myFunction with 1 2 myNumber +display myNumber crlf +call sayHello +call sayBye # 서브 프로시저는 선언되기 전에 호출될 수 있습니다. + +sub sayBye + display "Bye!" +end sub + +# LDPL의 가장 큰 특징 중 하나는 자신만의 +# 문장을 만들 수 있다는 것입니다. + +create statement "say hi" executing sayHello +say hi + +create statement "random add $ and $ in $" executing myFunction +random add 1 and 2 in myNumber +display myNumber crlf + +exit +``` + +## 더 읽을거리 + + * [LDPL 문서](https://docs.ldpl-lang.org) diff --git a/ko/lean4.md b/ko/lean4.md new file mode 100644 index 0000000000..3882c7c7e8 --- /dev/null +++ b/ko/lean4.md @@ -0,0 +1,478 @@ +--- +name: "Lean 4" +filename: learnlean4.lean +contributors: + - ["Balagopal Komarath", "https://bkomarath.rbgo.in/"] + - ["Ferinko", "https://github.com/Ferinko"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +[Lean 4](https://lean-lang.org/)는 종속 타입 함수형 프로그래밍 언어이자 대화형 정리 증명기입니다. + +```lean4 +/- +열거형 데이터 타입입니다. +-/ +inductive Grade where + | A : Grade + | B : Grade + | F : Grade +deriving Repr + +/- +함수입니다. +-/ +def grade (m : Nat) : Grade := + if 80 <= m then Grade.A + else if 60 <= m then Grade.B + else Grade.F + +def highMarks := 80 + 9 +def lowMarks := 25 + 25 +#eval grade highMarks +#eval grade lowMarks + +#check (0 : Nat) +/- #check (0 : Grade) -/ /- 이것은 오류입니다. -/ + +/- +타입 자체도 값입니다. +-/ +#check (Nat : Type) + +/- +수학적 명제는 Lean에서 값입니다. `Prop`은 명제의 타입입니다. + +다음은 몇 가지 간단한 명제입니다. +-/ + +#check 0 = 1 +#check 1 = 1 +#check 2^9 - 2^8 = 2^8 + +/- +Lean은 `0 = 1 : Prop`을 표시하여 다음을 말합니다: + + "0 = 1" 문장은 명제입니다. + +우리는 참인 명제와 거짓인 명제를 구별하고 싶습니다. 우리는 증명을 통해 이를 수행합니다. + +각 명제는 타입입니다. `0 = 1`은 타입이고, `1 = 1`은 다른 타입입니다. + +명제는 해당 타입의 값이 있는 경우에만 참입니다. + +`1 = 1` 타입의 값을 어떻게 생성할까요? 해당 타입에 대해 정의된 생성자를 사용합니다. + + `Eq.refl a`는 `a = a` 타입의 값을 생성합니다. (반사성) + +이를 사용하여 다음과 같이 `1 = 1`을 증명할 수 있습니다. +-/ + +theorem one_eq_one : 1 = 1 := Eq.refl 1 + +/- +하지만 `0 = 1`을 증명할 (타입의 값을 생성할) 방법은 없습니다. + +다음은 실패합니다. `Eq.refl 1`도 마찬가지입니다. +-/ + +/- theorem zero_eq_one : 0 = 1 := Eq.refl 0 -/ + +/- +변수를 포함하는 부등식을 증명해 봅시다. + +`calc` 프리미티브를 사용하면 단계별 계산을 통해 등식을 증명할 수 있습니다. 각 단계는 증명으로 정당화되어야 합니다. +-/ +theorem plus_squared (a b : Nat) : (a+b)^2 = a^2 + 2*a*b + b^2 := + calc + (a+b)^2 = (a+b)*(a+b) := Nat.pow_two _ + _ = (a+b)*a + (a+b)*b := Nat.mul_add _ _ _ + _ = a*a + b*a + (a*b + b*b) := by repeat rw [Nat.add_mul] + _ = a*a + b*a + a*b + b*b := by rw [← Nat.add_assoc] + _ = a*a + a*b + a*b + b*b := by rw [Nat.mul_comm b _] + _ = a^2 + a*b + a*b + b*b := by rw [← Nat.pow_two _] + _ = a^2 + a*b + a*b + b^2 := by rw [← Nat.pow_two _] + _ = a^2 + (a*b + a*b) + b^2 := by rw [Nat.add_assoc (a^_)] + _ = a^2 + 2*(a*b) + b^2 := by rw [← Nat.two_mul _] + _ = a^2 + 2*a*b + b^2 := by rw [Nat.mul_assoc _ _ _] +/- +일치시킬 대상에 모호함이 없을 때 밑줄을 사용할 수 있습니다. + +예를 들어, 첫 번째 단계에서 `Nat.pow_two (a+b)`를 적용하려고 합니다. 하지만, +`(a+b)`는 여기서 `Nat.pow_two`를 적용할 유일한 패턴입니다. 그래서 생략할 수 있습니다. +-/ + +/- +이제 더 "현실적인" 정리, 즉 논리적 연결사를 포함하는 정리를 증명해 봅시다. + +먼저, 짝수와 홀수를 정의합니다. +-/ +def Even (n : Nat) := ∃ k, n = 2*k +def Odd (n : Nat) := ∃ k, n = 2*k + 1 + +/- +존재 증명을 하려면, 우리가 안다면 특정 값을 제공할 수 있습니다. +-/ +theorem zero_even : Even 0 := + have h : 0 = 2 * 0 := Eq.symm (Nat.mul_zero 2) + Exists.intro 0 h +/- +`Exists.intro v h`는 `x`를 `v`로 치환하고 `p v`에 대한 증명 `h`를 사용하여 `∃ x, p x`를 증명합니다. +-/ + +/- +이제, 존재 가설을 사용하여 존재 결론을 증명하는 방법을 살펴보겠습니다. + +매개변수 `n`과 `m` 주위의 중괄호는 그것들이 암시적임을 나타냅니다. 여기서 Lean은 `hn`과 `hm`에서 그것들을 추론할 것입니다. +-/ +theorem even_mul_even_is_even' {n m : Nat} (hn : Even n) (hm : Even m) : Even (n*m) := + Exists.elim hn (fun k1 hk1 => + Exists.elim hm (fun k2 hk2 => + Exists.intro (k1 * ( 2 * k2)) ( + calc + n*m = (2 * k1) * (2 * k2) := by rw [hk1, hk2] + _ = 2 * (k1 * (2 * k2)) := by rw [Nat.mul_assoc] + ) + ) + ) + +/- +대부분의 증명은 *전술(tactics)*을 사용하여 작성됩니다. 이것들은 Lean이 스스로 증명을 구성하도록 안내하는 명령어입니다. + +전술을 사용하여 증명된 동일한 정리입니다. +-/ +theorem even_mul_even_is_even {n m : Nat} (hn : Even n) (hm : Even m) : Even (n*m) := by + have ⟨k1, hk1⟩ := hn + have ⟨k2, hk2⟩ := hm + apply Exists.intro $ k1 * (2 * k2) + calc + n*m = (2 * k1) * (2 * k2) := by rw [hk1, hk2] + _ = 2 * (k1 * (2 * k2)) := by rw [Nat.mul_assoc] + +/- +함의(implications)를 다루어 봅시다. +-/ +theorem succ_of_even_is_odd' {n : Nat} : Even n → Odd (n+1) := + fun hn => + have ⟨k, hk⟩ := hn + Exists.intro k ( + calc + n + 1 = 2 * k + 1 := by rw [hk] + ) +/- +함의 `p → q`를 증명하려면 `p`의 증명을 받아 `q`의 증명을 구성하는 함수를 작성해야 합니다. + +여기서 `pn`은 `Even n := ∃ k, n = 2 *k`의 증명입니다. 존재 한정자를 제거하면 `k`와 `n = 2 * k`의 증명 `hk`를 얻습니다. + +이제 존재 한정자 `∃ k, n + 1 = 2 * k + 1`을 도입해야 합니다. 이 `k`는 `n`에 대한 `k`와 동일합니다. 그리고 방정식은 `hk`에 의해 허용되는 `n`을 `2 * k`로 치환하는 간단한 계산으로 증명됩니다. +-/ + +/- +전술을 사용하여 동일한 정리입니다. +-/ +theorem succ_of_even_is_odd {n : Nat} : Even n → Odd (n+1) := by + intro hn + have ⟨k, hk⟩ := hn + apply Exists.intro k + rw [hk] + +/- +다음 정리는 비슷하게 증명될 수 있습니다. + +이 정리는 나중에 사용할 것입니다. + +`sorry`는 어떤 정리든 증명합니다. 실제 증명에서는 사용해서는 안 됩니다. +-/ +theorem succ_of_odd_is_even {n : Nat} : Odd n → Even (n+1) := sorry + +/- +정리를 적용하여 사용할 수 있습니다. +-/ +example : Odd 1 := by + apply succ_of_even_is_odd + exact zero_even +/- +두 가지 새로운 전술은 다음과 같습니다: + + - `apply p`는 `p`가 함의 `q → r`이고 `r`이 목표일 때 목표를 `q`로 다시 씁니다. 더 일반적으로 `apply t`는 현재 목표를 `t`의 결론과 통합하고 `t`의 각 가설에 대한 목표를 생성합니다. + - `exact h`는 목표가 `h`와 동일하다고 명시하여 목표를 해결합니다. +-/ + +/- +선언(disjunctions)의 예를 살펴봅시다. +-/ +example : Even 0 ∨ Odd 0 := Or.inl zero_even +example : Even 0 ∨ Odd 1 := Or.inl zero_even +example : Odd 1 ∨ Even 0 := Or.inr zero_even +/- +여기서, 우리는 항상 `p ∨ q`에서 `p`와/또는 `q` 중 어느 것이 올바른지 알 수 있습니다. 그래서 우리는 올바른 쪽의 증명을 도입할 수 있습니다. +-/ + +/- +더 "표준적인" 선언을 살펴봅시다. + +여기서, `n : Nat`라는 가설에서 `n`이 짝수인지 홀수인지 결정할 수 없습니다. 그래서 우리는 `Or`를 직접 구성할 수 없습니다. + +하지만, 어떤 특정한 `n`에 대해서는 어떤 것을 구성해야 할지 알게 될 것입니다. + +이것이 바로 귀납법이 우리에게 허용하는 것입니다. `induction` 전술을 도입합니다. + +귀납적 가설은 선언입니다. 가설에 선언이 나타나면 *철저한 사례별 증명*을 사용합니다. 이것은 `cases` 전술을 사용하여 수행됩니다. +-/ +theorem even_or_odd {n : Nat} : Even n ∨ Odd n := by + induction n + case zero => left ; exact zero_even + case succ n ihn => + cases ihn with + | inl h => right ; apply (succ_of_even_is_odd h) + | inr h => left ; apply (succ_of_odd_is_even h) +/- +`induction`은 자연수만을 위한 것이 아닙니다. Lean의 모든 타입은 귀납적이므로 모든 타입에 대해 사용할 수 있습니다. +-/ + +/- +이제 콜라츠 추측을 기술합니다. 증명은 독자를 위한 연습 문제로 남겨둡니다. +-/ +def collatz_next (n : Nat) : Nat := + if n % 2 = 0 then n / 2 else 3 * n + 1 + +def iter (k : Nat) (f : Nat → Nat) := + match k with + | Nat.zero => fun x => x + | Nat.succ k' => fun x => f (iter k' f x) + +theorem collatz : ∀ n, n > 0 → ∃ k, iter k collatz_next n = 1 := sorry + +/- +이제 논리학의 몇 가지 "코너 케이스"입니다. +-/ + +/- +명제 `True`는 자명하게 증명될 수 있는 것입니다. + +`True.intro`는 `True`를 증명하기 위한 생성자입니다. 입력이 필요 없다는 점에 유의하십시오. +-/ +theorem obvious : True := True.intro + +/- +반면에 `False`에 대한 생성자는 없습니다. + +`sorry`를 사용해야 합니다. +-/ +theorem impossible : False := sorry + +/- +가설에 있는 어떤 `False`라도 우리에게 무엇이든 결론 내리게 할 수 있습니다. + +항(term) 스타일로 작성할 때, 우리는 제거자 `False.elim`을 사용합니다. 이것은 `False`의 증명(여기서는 `h`)을 받아 목표가 무엇이든 결론을 내립니다. +-/ +theorem nonsense (h : False) : 0 = 1 := False.elim h + +/- +`contradiction` 전술은 가설에 있는 어떤 `False`라도 사용하여 목표를 결론 내립니다. +-/ +theorem more_nonsense (h : False) : 1 = 2 := by contradiction + +/- +구성주의 논리와 고전주의 논리의 차이를 설명하기 위해, 이제 대우(contrapositive) 정리를 증명합니다. + +순방향은 고전주의 논리를 요구하지 않습니다. +-/ +theorem contrapositive_forward' (p q : Prop) : (p → q) → (¬q → ¬p) := + fun pq => fun hqf => fun hp => hqf (pq hp) +/- +정의 `¬q := q → False`를 사용하십시오. `p → q`와 `q → False`가 주어졌을 때 `p → False`를 구성해야 한다는 점에 유의하십시오. 이것은 단지 함수 합성일 뿐입니다. +-/ + +/- +위 증명을 전술을 사용하여 다시 작성한 것입니다. +-/ +theorem contrapositive_forward (p q : Prop) : (p → q) → (¬q → ¬p) := by + intro hpq + intro + intro hp + specialize hpq hp + contradiction + +/- +역방향은 고전주의 논리를 요구합니다. + +여기서, 우리는 다음 타입의 값들이 주어졌을 때 `q`를 구성해야 합니다: + + - `(q → False) → (p → False)`. + - `p`. + +이것은 배중률을 사용하지 않고는 불가능합니다. +-/ +theorem contrapositive_reverse' (p q : Prop) : (¬q → ¬p) → (p → q) := + fun hnqnp => + Classical.byCases + (fun hq => fun _ => hq) + (fun hnq => fun hp => absurd hp (hnqnp hnq)) +/- +배중률은 우리에게 `q` 또는 `q → False`가 있을 것이라고 말해줍니다. 첫 번째 경우, `q`를 구성하는 것은 자명합니다. 우리는 이미 그것을 가지고 있습니다. 두 번째 경우, `p → False`를 얻기 위해 `q → False`를 제공합니다. 그런 다음, `p`와 `p → False`가 주어지면 `False`를 구성할 수 있다는 사실(구성주의 논리에서)을 사용합니다. 일단 `False`를 가지게 되면, 우리는 무엇이든 구성할 수 있으며, 구체적으로 `q`를 구성할 수 있습니다. +-/ + +/- +전술을 사용한 동일한 증명입니다. +-/ +theorem contrapositive_reverse (p q : Prop) : (¬q → ¬p) → (p → q) := by + intro hnqnp + intro hp + have emq := Classical.em q + cases emq + case inl _ => assumption + case inr h => specialize hnqnp h ; contradiction + +/- +공리 체계를 정의하고 작업하는 방법을 설명하기 위해, Herstein의 "대수학의 주제들(Topics in Algebra)" 제2판에서 직접 번역한 군(Group)의 정의와 몇 가지 증명을 소개합니다. +-/ + +/- +`section`은 네임스페이스를 도입합니다. +-/ +section GroupTheory +/- +군과 같은 추상적인 객체를 정의하기 위해 `class`를 사용할 수 있습니다. +-/ +class Group (G : Type u) where + op : G → G → G + assoc : ∀ a b c : G, op (op a b) c = op a (op b c) + e : G + identity: ∀ a : G, op a e = a ∧ op e a = a + inverse: ∀ a : G, ∃ b : G, op a b = e ∧ op b a = e + +/- +이것을 편리하게 만들기 위해 몇 가지 표기법을 도입합시다. +-/ +open Group +infixl:70 " * " => op + +/- +이 `section`에서 `G`는 항상 군을 나타내고 변수 `a b c`는 해당 군의 원소가 될 것입니다. +-/ +variable [Group G] {a b c : G} + +def is_identity (e' : G) := ∀ a : G, (a * e' = a ∧ e' * a = a) + +/- +항등원이 유일함을 증명합니다. +-/ +theorem identity_element_unique : ∀ e' : G, is_identity e' → e' = e := by + intro e' + intro h + specialize h e + have ⟨h1, _⟩ := h + have h' := identity e' + have ⟨_, h2⟩ := h' + exact Eq.trans (Eq.symm h2) h1 +/- +`identity` 공리를 사용했음에 유의하십시오. +-/ + +/- +왼쪽 소거. `Group`의 `identity`와 `inverse` 공리를 모두 사용해야 합니다. +-/ +theorem left_cancellation : ∀ x y : G, a * x = a * y → x = y := by + have h1 := inverse a + have ⟨ai, a_inv⟩ := h1 + have ⟨_, h2⟩ := a_inv + intro x y + intro h3 + calc + x = (e : G) * x := Eq.symm (identity x).right + _ = ai * a * x := by rw [h2] + _ = ai * (a * x) := by rw [assoc] + _ = ai * (a * y) := by rw [h3] + _ = ai * a * y := by rw [← assoc] + _ = (e : G) * y := by rw [h2] + _ = y := (identity y).right + +end GroupTheory /- 변수 `G`, `a`, `b`, `c`는 이제 범위에 없습니다. -/ + +/- +상호 재귀적인 정의를 살펴봅시다. + +두 개의 힙이 있는 님(Nim) 게임입니다. +-/ +abbrev between (lower what upper : Nat) : Prop := lower ≤ what ∧ what ≤ upper + +mutual + def Alice : Nat → Nat → Prop + | n1, n2 => + ∃ k, (between 1 k n1 ∧ (between 1 k n1 → Bob (n1-k) n2)) + ∨ (between 1 k n2 ∧ (between 1 k n2 → Bob n1 (n2-k))) + + def Bob : Nat → Nat → Prop + | n1, n2 => + ∀ k, (between 1 k n1 → Alice (n1-k) n2) + ∧ (between 1 k n2 → Alice n1 (n2-k)) +end + +example : Bob 0 0 := by + intro k + induction k + case zero => + constructor + intro ; contradiction + intro ; contradiction + case succ => + constructor + intro a ; have := a.right ; contradiction + intro a ; have := a.right ; contradiction + +/- +단지 `def`를 사용하여 함수가 정의될 때, 우리는 Lean에게 종료를 확신시켜야 합니다. 다음은 모든 후보 약수를 테스트하는 간단한 소수 판별 알고리즘입니다. +-/ +def prime' (n : Nat) : Bool := + if h : n < 2 then + false + else + @go 2 n (by omega) +where + go (d : Nat) (n : Nat) {_ : n ≥ d} : Bool := + if h : n = d then /- `omega`가 아래에서 필요로 하는 `h`. -/ + true + else if n % d = 0 then + false + else + @go (Nat.succ d) n (by omega) + termination_by (n - d) +/- +재귀 함수 `go`가 각 재귀 호출에서 `n-k`가 감소하기 때문에 종료된다는 것을 명시해야 합니다. 이를 위해서는 재귀 호출 위치에 `n > k`라는 가설이 필요합니다. 하지만 함수 자체는 `n ≥ k`라고만 가정할 수 있습니다. 우리는 `n ≤ k` 테스트를 `h`로 레이블을 지정하여, 나중에 `omega`가 `n > k`라고 결론 내리는 데 이 명제의 반증을 사용할 수 있도록 합니다. + +`omega` 전술은 간단한 등식과 부등식을 풀 수 있습니다. +-/ +/- +또한 `def` 앞에 `partial`을 붙여 Lean이 전체성(totality)을 확인하지 않도록 지시할 수 있습니다. +-/ + +/- +또는, 약수를 가장 큰 것부터 가장 작은 것 순으로 테스트하도록 함수를 다시 작성할 수 있습니다. 이 경우 Lean은 함수가 전체(total)임을 쉽게 확인합니다. +-/ +def prime (n : Nat) : Bool := + if n < 2 then + true + else + go (n-1) n +where + go d n := + if d < 2 then + true + else if n % d = 0 then + false + else + go (d-1) n +/- +이제 Lean에게는 각 재귀 호출에서 `d`가 감소하기 때문에 `go`가 종료될 것이라는 점이 명백합니다. +-/ +#eval prime 57 +#eval prime 97 +``` + +더 자세한 학습을 원하시면 다음을 참조하십시오: + +* [Functional Programming in Lean](https://lean-lang.org/functional_programming_in_lean/) +* [Theorem Proving in Lean 4](https://lean-lang.org/theorem_proving_in_lean4/) +* [Lean 4 Manual](https://lean-lang.org/lean4/doc/) diff --git a/ko/less.md b/ko/less.md new file mode 100644 index 0000000000..55168eef3f --- /dev/null +++ b/ko/less.md @@ -0,0 +1,385 @@ +--- +name: Less +filename: learnless.less +contributors: + - ["Saravanan Ganesh", "http://srrvnn.me"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Less는 변수, 중첩, 믹스인 등과 같은 기능을 추가하는 CSS 전처리기입니다. +Less (및 [Sass](http://sass-lang.com/)와 같은 다른 전처리기)는 개발자가 유지 관리가 가능하고 DRY(Don't Repeat Yourself) 코드를 작성하는 데 도움이 됩니다. + +```less +//한 줄 주석은 Less가 CSS로 컴파일될 때 제거됩니다. + +/*여러 줄 주석은 유지됩니다. */ + + + +/* 변수 +==============================*/ + + +/* 변수에 CSS 값(예: 색상)을 저장할 수 있습니다. + 변수를 만들려면 '@' 기호를 사용하십시오. */ + +@primary-color: #a3a4ff; +@secondary-color: #51527f; +@body-font: 'Roboto', sans-serif; + +/* 스타일시트 전체에서 변수를 사용할 수 있습니다. + 이제 색상을 변경하려면 한 번만 변경하면 됩니다.*/ + +body { + background-color: @primary-color; + color: @secondary-color; + font-family: @body-font; +} + +/* 이것은 다음과 같이 컴파일됩니다. */ + +body { + background-color: #a3a4ff; + color: #51527F; + font-family: 'Roboto', sans-serif; +} + + +/* 스타일시트 전체에서 나타날 때마다 색상을 변경하는 것보다 + 유지 관리가 훨씬 쉽습니다. */ + + + +/* 믹스인 +==============================*/ + + +/* 둘 이상의 요소에 대해 동일한 코드를 작성하고 있다는 것을 + 발견하면 쉽게 재사용하고 싶을 수 있습니다.*/ + +.center { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; +} + +/* 단순히 선택자를 스타일로 추가하여 믹스인을 사용할 수 있습니다. */ + +div { + .center; + background-color: @primary-color; +} + +/* 다음과 같이 컴파일됩니다. */ + +.center { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; +} +div { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + background-color: #a3a4ff; +} + +/* 선택자 뒤에 괄호를 추가하여 믹스인 코드가 컴파일되지 않도록 + 생략할 수 있습니다. */ + +.center() { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; +} + +div { + .center; + background-color: @primary-color; +} + +/* 다음과 같이 컴파일됩니다. */ +div { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + background-color: #a3a4ff; +} + + + +/* 중첩 +==============================*/ + + +/* Less를 사용하면 선택자 내에 선택자를 중첩할 수 있습니다. */ + +ul { + list-style-type: none; + margin-top: 2em; + + li { + background-color: #f00; + } +} + +/* '&'는 부모 선택자로 대체됩니다. */ +/* 의사 클래스를 중첩할 수도 있습니다. */ +/* 과도하게 중첩하면 코드를 유지 관리하기 어려워진다는 점을 명심하십시오. + 모범 사례는 중첩 시 3단계 이상으로 들어가지 않는 것을 권장합니다. + 예를 들어: */ + +ul { + list-style-type: none; + margin-top: 2em; + + li { + background-color: red; + + &:hover { + background-color: blue; + } + + a { + color: white; + } + } +} + +/* 컴파일 결과: */ + +ul { + list-style-type: none; + margin-top: 2em; +} + +ul li { + background-color: red; +} + +ul li:hover { + background-color: blue; +} + +ul li a { + color: white; +} + + + +/* 함수 +==============================*/ + + +/* Less는 다양한 작업을 수행하는 데 사용할 수 있는 함수를 제공합니다. + 다음을 고려하십시오. */ + +/* 함수는 이름을 사용하고 필요한 인수를 전달하여 호출할 수 있습니다. */ + +body { + width: round(10.25px); +} + +.header { + background-color: lighten(#000, 0.5); +} + +.footer { + background-color: fadeout(#000, 0.25) +} + +/* 컴파일 결과: */ + +body { + width: 10px; +} + +.header { + background-color: #010101; +} + +.footer { + background-color: rgba(0, 0, 0, 0.75); +} + +/* 자신만의 함수를 정의할 수도 있습니다. 함수는 믹스인과 매우 유사합니다. + 함수와 믹스인 중에서 선택할 때, 믹스인은 CSS를 생성하는 데 가장 적합하고 + 함수는 Less 코드 전체에서 사용할 수 있는 논리에 더 적합하다는 것을 + 기억하십시오. '수학 연산자' 섹션의 예제는 재사용 가능한 + 함수가 되기에 이상적인 후보입니다. */ + +/* 이 함수는 두 숫자의 평균을 계산합니다. */ + +.average(@x, @y) { + @average-result: ((@x + @y) / 2); +} + +div { + .average(16px, 50px); // 믹스인 "호출" + padding: @average-result; // "반환" 값 사용 +} + +/* 컴파일 결과: */ + +div { + padding: 33px; +} + + + +/* 확장 (상속) +==============================*/ + + +/* 확장은 한 선택자의 속성을 다른 선택자와 공유하는 방법입니다. */ + +.display { + height: 50px; +} + +.display-success { + &:extend(.display); + border-color: #22df56; +} + +/* 컴파일 결과: */ +.display, +.display-success { + height: 50px; +} +.display-success { + border-color: #22df56; +} + +/* 믹스인을 만드는 것보다 CSS 문을 확장하는 것이 좋습니다. + 모두 동일한 기본 스타일을 공유하는 클래스를 그룹화하는 방식 때문입니다. + 이것이 믹스인으로 수행되었다면 속성은 + 믹스인을 호출한 각 문에 대해 복제됩니다. + 워크플로에는 영향을 미치지 않지만 Less 컴파일러가 생성한 + 파일에 불필요한 부풀림을 추가합니다. */ + + + +/* 부분 및 가져오기 +==============================*/ + + +/* Less를 사용하면 부분 파일을 만들 수 있습니다. 이렇게 하면 Less + 코드를 모듈화하는 데 도움이 될 수 있습니다. 부분 파일은 관례적으로 + '_'로 시작합니다(예: _reset.less). 그리고 CSS로 컴파일되는 + 주요 less 파일로 가져옵니다. */ + +/* _reset.less라는 파일에 넣을 다음 CSS를 고려하십시오. */ + +html, +body, +ul, +ol { + margin: 0; + padding: 0; +} + +/* Less는 @import를 제공하여 부분 파일을 파일로 가져올 수 있습니다. + 이는 가져온 파일을 가져오기 위해 다른 HTTP 요청을 하는 + 전통적인 CSS @import 문과 다릅니다. Less는 + 가져온 파일을 가져와 컴파일된 코드와 결합합니다. */ + +@import 'reset'; + +body { + font-size: 16px; + font-family: Helvetica, Arial, Sans-serif; +} + +/* 컴파일 결과: */ + +html, body, ul, ol { + margin: 0; + padding: 0; +} + +body { + font-size: 16px; + font-family: Helvetica, Arial, Sans-serif; +} + + + +/* 수학 연산 +==============================*/ + + +/* Less는 +, -, *, /, % 연산자를 제공합니다. + 이러한 연산자는 수동으로 계산한 값을 사용하는 대신 + Less 파일에서 직접 값을 계산하는 데 유용할 수 있습니다. + 아래는 간단한 2열 디자인을 설정하는 예입니다. */ + +@content-area: 960px; +@main-content: 600px; +@sidebar-content: 300px; + +@main-size: @main-content / @content-area * 100%; +@sidebar-size: @sidebar-content / @content-area * 100%; +@gutter: 100% - (@main-size + @sidebar-size); + +body { + width: 100%; +} + +.main-content { + width: @main-size; +} + +.sidebar { + width: @sidebar-size; +} + +.gutter { + width: @gutter; +} + +/* 컴파일 결과: */ + +body { + width: 100%; +} + +.main-content { + width: 62.5%; +} + +.sidebar { + width: 31.25%; +} + +.gutter { + width: 6.25%; +} +``` + +## Less 연습 + +브라우저에서 Less를 사용해보고 싶다면 다음을 확인하십시오. +* [Codepen](http://codepen.io/) +* [LESS2CSS](http://lesscss.org/less-preview/) + +## 호환성 + +Less는 CSS로 컴파일하는 프로그램만 있으면 모든 프로젝트에서 사용할 수 있습니다. 사용 중인 CSS가 대상 브라우저와 호환되는지 확인해야 합니다. + +[QuirksMode CSS](http://www.quirksmode.org/css/) 및 [CanIUse](http://caniuse.com)는 호환성을 확인하는 데 유용한 자료입니다. + +## 더 읽을거리 +* [공식 문서](http://lesscss.org/features/) +* [Less CSS - 초보자 가이드](http://www.hongkiat.com/blog/less-basic/) diff --git a/ko/lfe.md b/ko/lfe.md new file mode 100644 index 0000000000..1ad351479c --- /dev/null +++ b/ko/lfe.md @@ -0,0 +1,445 @@ +--- +name: "Lisp Flavoured Erlang (LFE)" +filename: lispflavourederlang.lfe +contributors: + - ["Pratik Karki", "https://github.com/prertik"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Lisp Flavoured Erlang(LFE)는 함수형, 동시성, 범용 프로그래밍 언어이자 코어 얼랭(Core Erlang) 및 얼랭 가상 머신(BEAM) 위에 구축된 리스프 방언(Lisp-2)입니다. + +LFE는 [LFE](https://github.com/rvirding/lfe)에서 얻을 수 있습니다. +고전적인 시작점은 [LFE 문서](http://docs.lfe.io)입니다. + +```lisp +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; 0. 구문 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; 일반적인 형식. + +;; 리스프는 ATOM과 S-표현식이라는 두 가지 구문으로 구성됩니다. +;; `form`은 그룹화된 S-표현식으로 알려져 있습니다. + +8 ; 아톰; 자기 자신으로 평가됩니다. + +:ERLANG ; 아톰; 심볼 :ERLANG으로 평가됩니다. + +t ; true를 나타내는 또 다른 아톰입니다. + +(* 2 21) ; S-표현식입니다. + +'(8 :foo t) ; 또 다른 예시입니다. + + +;;; 주석 + +;; 한 줄 주석은 세미콜론으로 시작합니다. 일반 주석에는 두 개, +;; 섹션 주석에는 세 개, 파일 수준 주석에는 네 개를 사용하십시오. + +;; 블록 주석 + + #| 주석 텍스트 |# + +;;; 환경 + +;; LFE는 사실상의 표준입니다. + +;; 라이브러리는 얼랭 생태계에서 직접 사용할 수 있습니다. Rebar3가 빌드 도구입니다. + +;; LFE는 보통 텍스트 편집기(가급적 이맥스)와 REPL(Read Evaluate Print Loop)을 +;; 동시에 실행하여 개발합니다. REPL을 사용하면 시스템에서 "실시간"으로 +;; 실행 중인 프로그램을 대화식으로 탐색할 수 있습니다. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; 1. 리터럴 및 특수 구문 규칙 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;;; 정수 + +1234 -123 ; 일반 10진수 표기법 +#b0 #b10101 ; 2진수 표기법 +#0 #10101 ; 2진수 표기법 (대체 형식) +#o377 #o-111 ; 8진수 표기법 +#d123456789 #d+123 ; 명시적 10진수 표기법 +#xc0ffe 0x-01 ; 16진수 표기법 +#2r1010 #8r377 ; 명시적 밑을 사용한 표기법 (최대 36) +#\a #$ #\ä #\🐭 ; 문자 표기법 (값은 해당 문자의 유니코드 코드 포인트입니다) +#\x1f42d; ; 16진수 값을 사용한 문자 표기법 + +;;; 부동 소수점 숫자 +1.0 +2.0 -1.5 1.0e10 1.111e-10 + +;;; 문자열 + +"큰따옴표 사이의 모든 텍스트. \"나 다른 특수 문자(\n 등)는 이스케이프될 수 있습니다." +; 리스트 문자열 +"Cat: \x1f639;" ; 일반 글꼴의 문자열에 유니코드를 작성하고 세미콜론으로 끝냅니다. + +#"이것은 일부 \"이스케이프된\" 및 인용된 (\x1f639;) 문자가 있는 바이너리 문자열입니다 \n" +; 바이너리 문자열은 그냥 문자열이지만 VM에서 다르게 작동합니다. +; 다른 작성 방법으로는 #B("a"), #"a", #B(97)이 있습니다. + + +;;; 문자 이스케이프 + +\b ; => 백스페이스 +\t ; => 탭 +\n ; => 개행 +\v ; => 수직 탭 +\f ; => 폼 피드 +\r ; => 캐리지 리턴 +\e ; => 이스케이프 +\s ; => 공백 +\d ; => 삭제 + +;;; 바이너리 +;; 어떤 내용이든 바이너리를 만드는 데 사용됩니다. +#B((#"a" binary) (#"b" binary)) ; #"ab" (평가된 형식) + +;;; 리스트: () 또는 (foo bar baz) + +;;; 튜플은 #(value1 value2 ...) 형식으로 작성됩니다. 빈 튜플 #()도 유효합니다. + +;;; 맵은 #M(key1 value1 key2 value2 ...) 형식으로 작성됩니다. 빈 맵 #M()도 유효합니다. + +;;; 심볼: 파싱할 수 없는 것들. 예: foo, Foo, foo-bar, :foo +| foo | ; 수직 막대로 감싸서 심볼을 명시적으로 생성합니다. + +;;; 평가 + +;; #.(... 어떤 표현식 ...). 예: '#.(+ 1 1)은 표현식을 읽는 동안 (+ 1 1)을 +;; 평가하여 효과적으로 '2가 됩니다. + +;; LFE REPL에서의 리스트 컴프리헨션 + +lfe> (list-comp + ((<- x '(0 1 2 3))) + (trunc (math:pow 3 x))) + (1 3 9 27) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 2. 핵심 형식 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 이 형식들은 커먼 리스프(Common Lisp)와 스킴(Scheme)에서 볼 수 있는 것들과 동일합니다. + +(quote e) +(cons head tail) +(car e) +(cdr e) +(list e ... ) +(tuple e ... ) +(binary seg ... ) +(map key val ...), (map-get m k), (map-set m k v ...), (map-update m k v ...) + +(lambda (arg ...) ...) + (match-lambda + ((arg ... ) {{(when e ...)}} ...) ; 절 일치 + ... ) +(let ((pat {{(when e ...)}} e) + ...) + ... ) +(let-function ((name lambda|match-lambda) ; 지역 함수만 정의 + ... ) + ... ) +(letrec-function ((name lambda|match-lambda) ; 지역 함수만 정의 + ... ) + ... ) +(let-macro ((name lambda-match-lambda) ; 지역 매크로만 정의 + ...) + ...) +(progn ... ) +(if test true-expr {{false-expr}}) +(case e + (pat {{(when e ...)}} ...) + ... )) +(receive + (pat {{(when e ...)}} ... ) + ... + (after timeout ... )) +(catch ... ) +(try + e + {{(case ((pat {{(when e ...)}} ... ) + ... ))}} + {{(catch + ; 다음은 반드시 길이가 3인 튜플이어야 합니다! + (((tuple type value ignore) {{(when e ...)}} + ... ) + ... )}} + {{(after ... )}}) + +(funcall func arg ... ) +(call mod func arg ... ) - 얼랭 Mod:Func(Arg, ... ) 호출 +(define-module name declaration ... ) +(extend-module declaration ... ) - 모듈 및 선언 정의/확장 +(define-function name lambda|match-lambda) +(define-macro name lambda|match-lambda) - 최상위 수준에서 함수/매크로 정의 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 3. 매크로 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 매크로는 언어의 일부이며, 핵심 언어와 표준 라이브러리 위에 +;; 추상화를 생성하여 표현하고자 하는 것을 더 직접적으로 +;; 표현할 수 있도록 해줍니다. + +;; 최상위 함수 + +(defun name (arg ...) ...) + +;; 함수에 주석 추가하기 + +(defun name + "패턴 매칭 인수를 사용하는 최상위 함수" + ((argpat ...) ...) + ...) + +;; 최상위 매크로 + +(defmacro name (arg ...) ...) +(defmacro name arg ...) + +;; 패턴 매칭 인수를 사용하는 최상위 매크로 + +(defmacro name + ((argpat ...) ...) + ...) + +;; 스킴에서 영감을 받은 syntax-rules 형식을 사용하는 최상위 매크로 + +(defsyntax name + (pat exp) + ...) + +;;; 매크로 또는 syntax-rule 형식의 지역 매크로 + +(macrolet ((name (arg ... ) ... ) + ... ) + ... ) + +(syntaxlet ((name (pat exp) ...) + ...) + ...) + +;; CLISP와 유사 + +(prog1 ...) +(prog2 ...) + +;; 얼랭 LFE 모듈 + +(defmodule name ...) + +;; 얼랭 LFE 레코드 + +(defrecord name ...) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 4. 패턴과 가드 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 얼랭과 비교한 LFE에서의 패턴 사용 + +;; 얼랭 ;; LFE +;; {ok, X} (tuple 'ok x) +;; error 'error +;; {yes, [X|Xs]} (tuple 'yes (cons x xs)) +;; <<34,F/float>> (binary 34 (f float)) +;; [P|Ps]=All (= (cons p ps) all) + + _ ; => 패턴 매칭 시 신경 쓰지 않음 + + (= pattern1 pattern2) ; => 더 쉽고 나은 버전의 패턴 매칭 + +;; 가드 + +;; 패턴이 나타날 때마다 (let, case, receive, lc 등) 선택적으로 +;; (when test ...) 형식의 가드를 뒤따를 수 있습니다. + +(progn gtest ...) ;; => 가드 테스트의 시퀀스 +(if gexpr gexpr gexpr) +(type-test e) +(guard-bif ...) ;; => 가드 BIF, 산술, 불리언 및 비교 연산자 + +;;; REPL + +lfe>(set (tuple len status msg) #(8 ok "Trillian")) + #(8 ok "Trillian") +lfe>msg + "Trillian" + +;;; 가드 사용을 보여주는 프로그램 + +(defun right-number? + ((x) (when (orelse (== x 42) (== x 276709))) + 'true) + ((_) 'false)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 5. 함수 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; if를 사용하는 간단한 함수. + +(defun max (x y) + "max 함수." + (if (>= x y) x y)) + +;; 더 많은 절을 사용하는 동일한 함수 + +(defun max + "max 함수." + ((x y) (when (>= x y)) x) + ((x y) y)) + +;; 비슷한 스타일이지만 flet 또는 fletrec으로 정의된 지역 함수를 사용하는 동일한 함수 + +(defun foo (x y) + "max 함수." + (flet ((m (a b) "지역 주석." + (if (>= a b) a b))) + (m x y))) + +;; LFE는 Lisp-2이므로 변수와 함수에 대해 별도의 네임스페이스를 가집니다. +;; 변수와 함수/매크로는 모두 어휘적으로 범위가 지정됩니다. +;; 변수는 lambda, match-lambda 및 let에 의해 바인딩됩니다. +;; 함수는 최상위 defun, flet 및 fletrec에 의해 바인딩됩니다. +;; 매크로는 최상위 defmacro/defsyntax 및 macrolet/syntaxlet에 의해 바인딩됩니다. + +;; (funcall func arg ...)은 CL처럼 람다/매치-람다를 호출하는 데 사용됩니다. +;; 변수에 바인딩된 (funs)가 사용됩니다. + +;; apply를 위한 별도의 바인딩 및 특수. +apply _F (...), +apply _F/3 ( a1, a2, a3 ) + +;; 함수 헤드에서의 Cons'ing +(defun sum (l) (sum l 0)) + (defun sum + (('() total) total) + (((cons h t) total) (sum t (+ h total)))) + +;; 생성자 형식 대신 cons 리터럴 + (defun sum (l) (sum l 0)) + (defun sum + (('() total) total) + ((`(,h . ,t) total) (sum t (+ h total)))) + +;; 함수 헤드에서 레코드 매칭 + +(defun handle_info + (('ping (= (match-state remote-pid 'undefined) state)) + (gen_server:cast (self) 'ping) + `#(noreply ,state)) + (('ping state) + `#(noreply ,state))) + +;; 메시지 수신 + (defun universal-server () + (receive + ((tuple 'become func) + (funcall func)))) + +;; 메시지를 수신하는 또 다른 방법 + + (defun universal-server () + (receive + (`#(become ,func) + (funcall func)))) + +;; 특정 작업을 위한 완전한 함수 작성 + +(defun compose (f g) + (lambda (x) + (funcall f + (funcall g x)))) + +(defun check () + (let* ((sin-asin (compose #'sin/1 #'asin/1)) + (expected (sin (asin 0.5))) + (compose-result (funcall sin-asin 0.5))) + (io:format "Expected answer: ~p~n" (list expected)) + (io:format "Answer with compose: ~p~n" (list compose-result)))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 6. 동시성 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; 얼랭의 경량 "프로세스"에 의해 수행되는 메시지 전달. + +(defmodule messenger-back + (export (print-result 0) (send-message 2))) + +(defun print-result () + (receive + ((tuple pid msg) + (io:format "Received message: '~s'~n" (list msg)) + (io:format "Sending message to process ~p ...~n" (list pid)) + (! pid (tuple msg)) + (print-result)))) + +(defun send-message (calling-pid msg) + (let ((spawned-pid (spawn 'messenger-back 'print-result ()))) + (! spawned-pid (tuple calling-pid msg)))) + +;; 다중 동시 HTTP 요청: + +(defun parse-args (flag) + "하나 이상의 명령줄 인수가 주어지면 전달된 값을 추출합니다. + + 예를 들어, 명령줄을 통해 다음이 전달된 경우: + + $ erl -my-flag my-value-1 -my-flag my-value-2 + + 그런 다음 LFE 프로그램에서 이 함수를 호출하여 추출할 수 있습니다: + + (let ((args (parse-args 'my-flag))) + ... + ) + 이 예에서 arg 변수에 할당된 값은 my-value-1 및 my-value-2 값을 + 포함하는 리스트가 됩니다." + (let ((`#(ok ,data) (init:get_argument flag))) + (lists:merge data))) + +(defun get-pages () + "인수가 없으면 'url 매개변수가 명령줄을 통해 전달되었다고 가정합니다." + (let ((urls (parse-args 'url))) + (get-pages urls))) + +(defun get-pages (urls) + "inets를 시작하고 (잠재적으로 많은) HTTP 요청을 만듭니다." + (inets:start) + (plists:map + (lambda (x) + (get-page x)) urls)) + +(defun get-page (url) + "단일 HTTP 요청을 만듭니다." + (let* ((method 'get) + (headers '()) + (request-data `#(,url ,headers)) + (http-options ()) + (request-options '(#(sync false)))) + (httpc:request method request-data http-options request-options) + (receive + (`#(http #(,request-id #(error ,reason))) + (io:format "Error: ~p~n" `(,reason))) + (`#(http #(,request-id ,result)) + (io:format "Result: ~p~n" `(,result)))))) +``` + +## 더 읽을거리 + +* [LFE DOCS](http://docs.lfe.io) +* [LFE GitBook](https://lfe.gitbooks.io/reference-guide/index.html) +* [LFE Wiki](https://en.wikipedia.org/wiki/LFE_(programming_language)) + +## 추가 정보 + +* [LFE PDF](http.www.erlang-factory.com/upload/presentations/61/Robertvirding-LispFlavouredErlang.pdf) +* [LFE mail](https://groups.google.com/d/msg/lisp-flavoured-erlang/XA5HeLbQQDk/TUHabZCHXB0J) diff --git a/ko/linker.md b/ko/linker.md new file mode 100644 index 0000000000..274e26ed80 --- /dev/null +++ b/ko/linker.md @@ -0,0 +1,199 @@ +--- +category: tool +name: Linker script +contributors: + - ["Alexander Kovalchuk", "https://github.com/Zamuhrishka"] +translators: + - ["Anuj Shah", "https://github.com/ShahAnuj2610"] +filename: learn.ld +--- + +**위치 카운터** - 링커에는 특수 변수가 있습니다. +"`.`" (점)은 항상 현재 출력 위치를 포함합니다. + +`ADDR (section)` - 지정된 섹션의 절대 주소를 반환합니다. 그러나 +이 섹션은 ADDR 함수를 사용하기 전에 정의되어야 합니다. + +`ALIGN (exp)` - exp 표현식 다음의 경계에 정렬된 위치 카운터의 값을 +반환합니다. + +`SIZEOF (section)` - 섹션의 크기를 바이트 단위로 반환합니다. + +`FILL (param)` - 현재 섹션의 채우기 패턴을 정의합니다. 모든 +섹션 내의 다른 지정되지 않은 영역은 함수 인수에 표시된 값으로 +채워집니다. + +`KEEP (param)` - param을 치명적인 것으로 표시하는 데 사용됩니다. + +`ENTRY (func)` - 프로그램의 진입점이 될 함수를 +정의합니다. + +```bash +# 프로그램의 진입점을 결정합니다. +ENTRY(Reset_Handler) + +# 스택의 맨 위 주소를 포함하는 변수를 정의합니다. +_estack = 0x20020000; +# 힙 크기 값을 포함하는 변수를 정의합니다. +_Min_Heap_Size = 0x200; +# 스택 크기 값을 포함하는 변수를 정의합니다. +_Min_Stack_Size = 0x400; + +# 이 프로세서에서 사용할 수 있는 메모리 카드 설명 +# MEMORY +# { +# MEMORY_DOMAIN_NAME (액세스 권한) : ORIGIN = START_ADDRESS, LENGTH = SIZE +# } +# 이 예에서 컨트롤러에는 세 개의 메모리 영역이 있습니다: +# RAM - 주소 0x20000000에서 시작하여 128KB를 차지합니다. +# CCMRAM - 주소 0x10000000에서 시작하여 64KB를 차지합니다. +# FLASH - 주소 0x8000000에서 시작하여 1024KB를 차지합니다. +# 또한 RAM 메모리 액세스는 읽기, 쓰기 및 실행이 가능합니다. +# CCMRAM 메모리는 읽기-쓰기만 가능합니다. +# FLASH 메모리는 읽기 및 실행이 가능합니다. +MEMORY +{ + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K + CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K + FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K +} + +# 출력 섹션을 설명합니다. +SECTIONS +{ + # 첫 번째 섹션에는 인터럽트 벡터 테이블이 포함됩니다. + .isr_vector : + { + # 현재 위치를 4바이트 경계에 맞춥니다. + . = ALIGN(4); + + # 사용하지 않는 입력 섹션에서 가비지를 수집할 수 있는 --gc-sections 옵션이 있습니다. + # 그리고 가비지 수집기가 건드리지 않아야 하는 섹션이 있는 경우 + # KEEP () 함수의 인수로 지정해야 합니다 (volatile 키워드와 유사). + # 항목 (* (. Isr_vector))은 모든 객체 파일의 .isr_vector 섹션을 의미합니다. 왜냐하면 + # 일반적으로 섹션에 대한 호소는 다음과 같이 보입니다: (FILE_NAME (SECTION_NAME)) + KEEP(*(.isr_vector)) + + # 현재 위치를 4바이트 경계에 맞춥니다. + . = ALIGN(4); + + # 표현식 "> MEMORY AREA"는 어떤 메모리 영역이 배치될지를 나타냅니다. + # 이 섹션. 이 섹션에서 .isr_vector 섹션은 FLASH 메모리에 위치합니다. + } >FLASH + +# 총계: 인터럽트 벡터 테이블을 포함하는 .isr_vector 섹션은 +# 4바이트 경계에 정렬되고, 가비지 수집기에 액세스할 수 없는 것으로 표시되며, 맨 처음에 배치됩니다. +# FLASH 마이크로컨트롤러 메모리. + + # 두 번째 섹션에는 프로그램 코드가 포함됩니다. + .text : + { + # 현재 위치를 4바이트 경계에 맞춥니다. + . = ALIGN(4); + + # 이 섹션에서 모든 객체 파일의 .text 영역을 + # 나타냅니다. + *(.text) + *(.text*) + + # 가비지 수집기로부터 .init 및 .fini 섹션을 보호합니다. + KEEP (*(.init)) + KEEP (*(.fini)) + + # 현재 위치를 4바이트 경계에 맞춥니다. + . = ALIGN(4); + + # 변수 _etext는 .text 섹션의 끝 주소를 저장하는 변수로 정의되며, + # 프로그램 소스 코드에서 다음 선언을 통해 사용할 수 있습니다. + # volaile unsigned int extern _etext; + _etext = .; + } >FLASH + +# 총계: 프로그램 코드를 포함하는 .text 섹션은 4바이트 경계에 정렬되고, +# 모든 객체 파일의 모든 프로그램 코드 섹션과 모든 객체 파일의 .init 및 .fini 섹션의 +# 가비지 수집기로부터 보호되며, 벡터 테이블 바로 뒤에 FLASH +# 마이크로컨트롤러 메모리에 위치합니다. +# text, .init 및 .fini 섹션은 스크립트에 선언된 순서대로 +# 메모리에 위치합니다. + + # 세 번째 섹션에는 상수 데이터가 포함됩니다. + .rodata : + { + # 현재 위치를 4바이트 경계에 맞춥니다. + . = ALIGN(4); + + # 이 섹션 영역 .rodata가 저장될 것임을 나타냅니다. + # 객체 파일 + *(.rodata) + *(.rodata*) + + # 현재 위치를 4바이트 경계에 맞춥니다. + . = ALIGN(4); + } >FLASH + + # _sidata 변수에 .data 섹션의 절대 주소를 저장합니다. + _sidata = LOADADDR(.data); + + # 네 번째 섹션에는 초기화된 변수가 포함됩니다. + .data : + { + # 현재 위치를 4바이트 경계에 맞춥니다. + . = ALIGN(4); + + # 현재 위치(섹션 시작)의 주소를 _sdata 변수에 저장합니다. + _sdata = .; + + # 이 섹션에서 모든 객체 파일의 .data 영역을 + # 나타냅니다. + *(.data) + *(.data*) + + # 현재 위치를 4바이트 경계에 맞춥니다. + . = ALIGN(4); + + # 현재 위치(섹션 끝)의 주소를 _sdata 변수에 저장합니다. + _edata = .; + + # AT 함수는 이 섹터가 한 메모리 영역에 저장됨을 나타냅니다. + # (이 경우 FLASH), 다른 메모리 영역(이 경우 RAM)에서 실행됩니다. + # 두 가지 유형의 주소가 있습니다: + # * VMA (가상 메모리 주소) - 컴파일러가 예상하는 런타임 주소입니다. + # 데이터를 봅니다. + # * LMA (로드 메모리 주소)는 링커가 데이터를 저장하는 주소입니다. + + # 시작 코드는 .data 섹션을 LMA 주소에서 VMA 주소로 복사해야 합니다. + + } >RAM AT> FLASH + + # 다섯 번째 섹션에는 0으로 초기화된 변수가 포함됩니다. + .bss : + { + # 현재 위치(섹션 시작)의 주소를 _sbss 및 __bss_start__ 변수에 저장합니다. + _sbss = .; + __bss_start__ = _sbss; + + # 이 섹션에서 모든 객체 파일의 .bss 영역을 + # 나타냅니다. + *(.bss) + *(.bss*) + + # 현재 위치를 4바이트 경계에 맞춥니다. + . = ALIGN(4); + + # 현재 위치(섹션 시작)의 주소를 _ebss 및 __bss_end__ 변수에 저장합니다. + _ebss = .; + __bss_end__ = _ebss; + } >RAM + + # 여섯 번째 섹션에는 힙과 스택이 포함됩니다. RAM의 맨 끝에 위치합니다. + ._user_heap_stack : + { + . = ALIGN(4); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(4); + } >RAM +} +``` diff --git a/ko/livescript.md b/ko/livescript.md new file mode 100644 index 0000000000..597c1fcf89 --- /dev/null +++ b/ko/livescript.md @@ -0,0 +1,324 @@ +--- +name: LiveScript +filename: learnLivescript.ls +contributors: + - ["Christina Whyte", "http://github.com/kurisuwhyte/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +LiveScript는 함수형 자바스크립트 컴파일 언어로, 호스트 언어와 대부분의 기본 의미를 공유합니다. 커링, 함수 합성, 패턴 매칭 및 Haskell, F#, Scala와 같은 언어에서 많이 차용한 다른 좋은 기능들이 추가되었습니다. + +LiveScript는 [Coco](https://github.com/satyr/coco)의 포크이며, Coco 자체는 [CoffeeScript](https://coffeescript.org/)의 포크입니다. + +```livescript +# CoffeeScript 사촌과 마찬가지로 LiveScript는 한 줄 주석에 숫자 기호를 사용합니다. + +/* + 여러 줄 주석은 C 스타일로 작성됩니다. 주석을 자바스크립트 출력에 + 보존하고 싶다면 사용하십시오. + */ + +# 구문 측면에서 LiveScript는 블록을 구분하기 위해 중괄호 대신 +# 들여쓰기를 사용하고, 함수를 적용하기 위해 괄호 대신 공백을 사용합니다. + + +######################################################################## +## 1. 기본 값 +######################################################################## + +# 값의 부재는 `undefined` 키워드 대신 `void`로 정의됩니다. +void # `undefined`와 같지만 더 안전합니다 (재정의할 수 없음) + +# 유효하지 않은 값은 Null로 표시됩니다. +null + + +# 가장 기본적인 실제 값은 논리 타입입니다: +true +false + +# 그리고 같은 의미를 가진 많은 별칭이 있습니다: +on; off +yes; no + + +# 그 다음은 숫자입니다. JS에서와 같이 배정밀도 부동 소수점입니다. +10 +0.4 # 참고: 앞에 `0`이 필요합니다. + +# 가독성을 위해 숫자에 밑줄과 문자 접미사를 사용할 수 있으며, +# 컴파일러는 이를 무시합니다. +12_344km + + +# 문자열은 JS에서와 같이 불변의 문자 시퀀스입니다: +"Christina" # 작은따옴표도 괜찮습니다! +"""여러 줄 + 문자열도 + 괜찮습니다.""" + +# 때로는 키워드를 인코딩하고 싶을 때가 있는데, 백슬래시 표기법을 사용하면 +# 쉽게 할 수 있습니다: +\keyword # => 'keyword' + + +# 배열은 순서가 있는 값의 컬렉션입니다. +fruits = + * \apple + * \orange + * \pear + +# 대괄호를 사용하여 더 간결하게 표현할 수 있습니다: +fruits = [ \apple, \orange, \pear ] + +# 공백을 사용하여 항목을 구분하는 편리한 방법으로 +# 문자열 목록을 만들 수도 있습니다. +fruits = <[ apple orange pear ]> + +# 0부터 시작하는 인덱스로 항목을 검색할 수 있습니다: +fruits[0] # => "apple" + +# 객체는 순서가 없는 키/값 쌍의 컬렉션이며, 몇 가지 다른 +# 기능도 있습니다 (나중에 자세히 설명). +person = + name: "Christina" + likes: + * "kittens" + * "and other cute stuff" + +# 다시 말하지만, 중괄호로 간결하게 표현할 수 있습니다: +person = {name: "Christina", likes: ["kittens", "and other cute stuff"]} + +# 키로 항목을 검색할 수 있습니다: +person.name # => "Christina" +person["name"] # => "Christina" + + +# 정규 표현식은 자바스크립트와 동일한 구문을 사용합니다: +trailing-space = /\s$/ # 대시-단어는 대시단어(dashedWords)가 됩니다. + +# 단, 여러 줄 표현식도 가능합니다! +# (주석과 공백은 무시됩니다) +funRE = // + function\s+(.+) # 이름 + \s* \((.*)\) \s* # 인수 + { (.*) } # 본문 + // + + +######################################################################## +## 2. 기본 연산 +######################################################################## + +# 산술 연산자는 자바스크립트와 동일합니다: +1 + 2 # => 3 +2 - 1 # => 1 +2 * 3 # => 6 +4 / 2 # => 2 +3 % 2 # => 1 + + +# 비교도 대부분 동일하지만, `==`는 JS의 `===`와 같고, +# JS의 `==`는 LiveScript에서 `~=`이며, `===`는 +# 객체 및 배열 비교와 더 엄격한 비교를 가능하게 합니다: +2 == 2 # => true +2 == "2" # => false +2 ~= "2" # => true +2 === "2" # => false + +[1,2,3] == [1,2,3] # => false +[1,2,3] === [1,2,3] # => true + ++0 == -0 # => true ++0 === -0 # => false + +# 다른 관계 연산자에는 <, <=, > 및 >=가 포함됩니다. + +# 논리 값은 `or`, `and`, `not` 논리 연산자를 통해 결합할 수 있습니다. +true and false # => false +false or true # => true +not false # => true + + +# 컬렉션에는 몇 가지 멋진 추가 연산자도 있습니다. +[1, 2] ++ [3, 4] # => [1, 2, 3, 4] +'a' in <[ a b c ]> # => true +'name' of { name: 'Chris' } # => true + + +######################################################################## +## 3. 함수 +######################################################################## + +# LiveScript는 함수형이므로 함수가 멋지게 처리될 것으로 기대할 수 있습니다. +# LiveScript에서는 함수가 일급이라는 것이 더욱 분명합니다: +add = (left, right) -> left + right +add 1, 2 # => 3 + +# 인수가 없는 함수는 느낌표로 호출됩니다! +two = -> 2 +two! + +# LiveScript는 자바스크립트와 마찬가지로 함수 범위를 사용하며 적절한 +# 클로저도 있습니다. 자바스크립트와 달리 `=`는 선언 연산자로 +# 작동하며 항상 왼쪽 변수를 선언합니다. + +# `:=` 연산자는 부모 범위의 이름을 *재사용*하는 데 사용할 수 있습니다. + + +# 복잡한 데이터 구조 내의 흥미로운 값에 빠르게 접근하기 위해 +# 함수의 인수를 분해할 수 있습니다: +tail = ([head, ...rest]) -> rest +tail [1, 2, 3] # => [2, 3] + +# 이항 또는 단항 연산자를 사용하여 인수를 변환할 수도 있습니다. +# 기본 인수도 가능합니다. +foo = (a = 1, b = 2) -> a + b +foo! # => 3 + +# 예를 들어 부작용을 피하기 위해 특정 인수를 복제하는 데 사용할 수 있습니다: +copy = (^^target, source) -> + for k,v of source => target[k] = v + target +a = { a: 1 } +copy a, { b: 2 } # => { a: 1, b: 2 } +a # => { a: 1 } + + +# 짧은 화살표 대신 긴 화살표를 사용하여 함수를 커링할 수 있습니다: +add = (left, right) --> left + right +add1 = add 1 +add1 2 # => 3 + +# 함수는 선언하지 않아도 암시적인 `it` 인수를 갖습니다. +identity = -> it +identity 1 # => 1 + +# 연산자는 LiveScript에서 함수가 아니지만 쉽게 함수로 바꿀 수 있습니다! +# 연산자 섹셔닝을 입력하십시오: +divide-by-two = (/ 2) +[2, 4, 8, 16].map(divide-by-two) .reduce (+) + + +# LiveScript는 함수 적용뿐만 아니라, 좋은 함수형 언어에서처럼 +# 함수를 합성하는 기능을 제공합니다: +double-minus-one = (- 1) . (* 2) + +# 일반적인 `f . g` 수학 공식 외에도 `>>` 및 `<<` 연산자를 사용하여 +# 함수를 통한 값의 흐름을 설명할 수 있습니다. +double-minus-one = (* 2) >> (- 1) +double-minus-one = (- 1) << (* 2) + + +# 값의 흐름에 대해 말하자면, LiveScript는 값을 함수에 적용하는 +# `|>` 및 `<|` 연산자를 사용합니다: +map = (f, xs) --> xs.map f +[1 2 3] |> map (* 2) # => [2 4 6] + +# 밑줄(_)로 위치를 표시하여 값을 배치할 위치를 선택할 수도 있습니다: +reduce = (f, xs, initial) --> xs.reduce f, initial +[1 2 3] |> reduce (+), _, 0 # => 6 + + +# 밑줄은 일반 부분 적용에도 사용되며, 모든 함수에 사용할 수 있습니다: +div = (left, right) -> left / right +div-by-two = div _, 2 +div-by-two 4 # => 2 + + +# 마지막으로, LiveScript에는 백-콜(back-calls)이 있어 일부 콜백 기반 코드에 +# 도움이 될 수 있습니다(그러나 Promises와 같은 더 기능적인 접근 방식을 +# 시도해야 합니다): +readFile = (name, f) -> f name +a <- readFile 'foo' +b <- readFile 'bar' +console.log a + b + +# 다음과 동일: +readFile 'foo', (a) -> readFile 'bar', (b) -> console.log a + b + + +######################################################################## +## 4. 패턴, 가드 및 제어 흐름 +######################################################################## + +# `if...else` 표현식으로 계산을 분기할 수 있습니다: +x = if n > 0 then \positive else \negative + +# `then` 대신 `=>`를 사용할 수 있습니다. +x = if n > 0 => \positive + else \negative + +# 복잡한 조건은 `switch` 표현식으로 더 잘 표현됩니다: +y = {} +x = switch + | (typeof y) is \number => \number + | (typeof y) is \string => \string + | 'length' of y => \array + | otherwise => \object # `otherwise`와 `_`는 항상 일치합니다. + +# 함수 본문, 선언 및 할당은 무료 `switch`를 얻으므로 +# 다시 입력할 필요가 없습니다: +take = (n, [x, ...xs]) --> + | n == 0 => [] + | _ => [x] ++ take (n - 1), xs + + +######################################################################## +## 5. 컴프리헨션 +######################################################################## + +# 목록 및 객체를 다루기 위한 함수형 도우미는 자바스크립트의 표준 라이브러리에 +# 바로 있으며(그리고 LiveScript의 "표준 라이브러리"인 prelude-ls에서 보완됨), +# 컴프리헨션을 사용하면 일반적으로 이러한 작업을 더 빠르고 멋진 구문으로 +# 수행할 수 있습니다: +oneToTwenty = [1 to 20] +evens = [x for x in oneToTwenty when x % 2 == 0] + +# `when`과 `unless`는 컴프리헨션에서 필터로 사용할 수 있습니다. + +# 객체 컴프리헨션은 배열 대신 객체를 반환한다는 점을 제외하고는 +# 동일한 방식으로 작동합니다: +copy = { [k, v] for k, v of source } + + +######################################################################## +## 4. OOP +######################################################################## + +# LiveScript는 대부분의 측면에서 함수형 언어이지만, 명령형 및 +# 객체 지향 프로그래밍을 위한 몇 가지 좋은 기능도 있습니다. 그 중 하나는 +# CoffeeScript에서 상속받은 클래스 구문과 클래스 슈가입니다: +class Animal + (@name, kind) -> + @kind = kind + action: (what) -> "*#{@name} (a #{@kind}) #{what}*" + +class Cat extends Animal + (@name) -> super @name, 'cat' + purr: -> @action 'purrs' + +kitten = new Cat 'Mei' +kitten.purr! # => "*Mei (a cat) purrs*" + +# 고전적인 단일 상속 패턴 외에도 클래스에 원하는 만큼 많은 +# 믹스인을 제공할 수 있습니다. 믹스인은 일반 객체일 뿐입니다: +Huggable = + hug: -> @action 'is hugged' + +class SnugglyCat extends Cat implements Huggable + +kitten = new SnugglyCat 'Purr' +kitten.hug! # => "*Mei (a cat) is hugged*" +``` + +## 더 읽을거리 + +LiveScript에 대해 더 많은 것이 있지만, 이것으로 작은 기능적인 것들을 +작성하기 시작하기에 충분할 것입니다. +[공식 웹사이트](http://livescript.net/)에는 언어에 대한 많은 정보와 +시도해 볼 수 있는 멋진 온라인 컴파일러가 있습니다! + +[prelude.ls](http://gkz.github.io/prelude-ls/)를 사용해보고, Freenode +네트워크의 `#livescript` 채널을 확인해 볼 수도 있습니다. diff --git a/ko/logtalk.md b/ko/logtalk.md new file mode 100644 index 0000000000..d0ff6ffe38 --- /dev/null +++ b/ko/logtalk.md @@ -0,0 +1,553 @@ +--- +name: Logtalk +filename: learnlogtalk.lgt +contributors: + - ["Paulo Moura", "http://github.com/pmoura"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Logtalk는 선언적 프로그래밍 기능을 손상시키지 않으면서 최신 코드 캡슐화 및 코드 재사용 메커니즘으로 Prolog를 확장하고 활용하는 객체 지향 논리 프로그래밍 언어입니다. Logtalk는 이식성이 뛰어난 코드로 구현되었으며 대부분의 최신 표준 준수 Prolog 구현을 백엔드 컴파일러로 사용할 수 있습니다. + +이 튜토리얼은 적절한 크기를 유지하기 위해 독자가 Prolog에 대한 실무 지식을 가지고 있으며 Logtalk 객체 지향 기능을 설명하는 데 편향되어 있다고 가정합니다. + +# 구문 + +Logtalk는 원활한 학습 곡선과 넓은 이식성을 위해 몇 가지 연산자와 지시어를 추가한 표준 Prolog 구문을 사용합니다. 한 가지 중요한 결과는 Prolog 코드를 거의 또는 전혀 변경하지 않고 객체에 쉽게 캡슐화할 수 있다는 것입니다. 또한 Logtalk는 대부분의 Prolog 모듈을 Logtalk 객체로 투명하게 해석할 수 있습니다. + +주요 연산자는 다음과 같습니다: + +* `::/2` - 객체에 메시지 보내기 +* `::/1` - _self_에 메시지 보내기 (즉, 처리 중인 메시지를 받은 객체에) +* `^^/1` - _super_ 호출 (상속되거나 가져온 술어의) + +가장 중요한 엔티티 및 술어 지시어 중 일부는 다음 섹션에서 소개됩니다. + +# 엔티티와 역할 + +Logtalk는 _객체_, _프로토콜_, _카테고리_를 일급 엔티티로 제공합니다. 엔티티 간의 관계는 _코드 재사용 패턴_과 엔티티가 수행하는 _역할_을 정의합니다. 예를 들어, 한 객체가 다른 객체를 _인스턴스화_할 때 첫 번째 객체는 인스턴스 역할을 하고 두 번째 객체는 클래스 역할을 합니다. 두 객체 간의 _확장_ 관계는 두 객체 모두 프로토타입 역할을 하며, 그중 하나가 다른 하나, 즉 부모 프로토타입을 확장함을 의미합니다. + +# 객체 정의하기 + +객체는 술어 선언과 정의를 캡슐화합니다. 객체는 동적으로 생성될 수 있지만 일반적으로 정적이며 소스 파일에 정의됩니다. 단일 소스 파일에는 여러 엔티티 정의가 포함될 수 있습니다. 리스트 멤버 공개 술어를 정의하는 간단한 객체: + +```logtalk +:- object(list). + + :- public(member/2). + member(Head, [Head| _]). + member(Head, [_| Tail]) :- + member(Head, Tail). + +:- end_object. +``` + +# 소스 파일 컴파일 및 로드 + +위의 `list` 객체 코드가 `list.lgt` 파일에 저장되어 있다고 가정하면, `logtalk_load/1` 내장 술어 또는 그 약어인 `{}/1`을 사용하여 파일 경로를 인수로 전달하여 컴파일하고 로드할 수 있습니다(확장자는 생략 가능). + +```logtalk +?- {list}. +yes +``` + +일반적으로 엔티티는 다른 소스 파일(예: 라이브러리 엔티티)에 정의된 엔티티에 대한 종속성을 가질 수 있습니다. 파일과 모든 종속성을 로드하려면, 애플리케이션에 필요한 모든 파일을 로드하는 _로더_ 파일을 정의하는 것이 좋습니다. 로더 파일은 일반적으로 `loader.lgt`라는 이름의 소스 파일이며, 이식성 및 표준 준수를 위해 일반적으로 `initialization/1` 지시어에서 `logtalk_load/1-2` 내장 술어를 호출합니다. 로더 파일은 모든 라이브러리, 도구 및 예제에 제공됩니다. + +# 객체에 메시지 보내기 + +`::/2` 중위 연산자는 객체에 메시지를 보내는 데 사용됩니다. Prolog에서와 같이 대체 솔루션을 위해 백트랙할 수 있습니다: + +```logtalk +?- list::member(X, [1,2,3]). +X = 1 ; +X = 2 ; +X = 3 +yes +``` + +캡슐화가 적용됩니다. 술어는 _public_, _protected_ 또는 _private_으로 선언될 수 있습니다. 범위 지시어가 없는 경우 _local_일 수도 있습니다. 예: + +```logtalk +:- object(scopes). + + :- private(bar/0). + bar. + + local. + +:- end_object. +``` + +객체가 `scopes.lgt` 파일에 저장되어 있다고 가정합니다: + +```logtalk +?- {scopes}. +yes + +?- catch(scopes::bar, Error, true). +Error = error( + permission_error(access, private_predicate, bar/0), + logtalk(scopes::bar, user) +) +yes + +?- catch(scopes::local, Error, true). +Error = error( + existence_error(predicate_declaration, local/0), + logtalk(scopes::local, user) +) +yes +``` + +메시지의 술어가 객체에 대해 알려지지 않은 경우(객체가 수행하는 역할이 조회 절차를 결정함)에도 오류가 발생합니다. 예: + +```logtalk +?- catch(scopes::unknown, Error, true). +Error = error( + existence_error(predicate_declaration, unknown/0), + logtalk(scopes::unknown, user) +) +yes +``` + +미묘한 점은 술어 범위 지시어는 술어 _호출_ 의미를 지정하며 _정의_ 의미가 아니라는 것입니다. 예를 들어, 클래스 역할을 하는 객체가 술어를 private으로 선언하면, 해당 술어는 서브클래스와 인스턴스에서 정의될 수 있지만, 해당 인스턴스에서는 클래스 _에서만_ 호출할 수 있습니다. + +# 프로토콜 정의 및 구현 + +프로토콜에는 여러 객체 및 카테고리에서 구현할 수 있는 술어 선언이 포함됩니다: + +```logtalk +:- protocol(listp). + + :- public(member/2). + +:- end_protocol. + +:- object(list, + implements(listp)). + + member(Head, [Head| _]). + member(Head, [_| Tail]) :- + member(Head, Tail). + +:- end_object. +``` + +프로토콜 술어의 범위는 protected 또는 private 구현을 사용하여 제한할 수 있습니다. 예: + +```logtalk +:- object(stack, + implements(private::listp)). + +:- end_object. +``` + +실제로 모든 엔티티 관계(엔티티 열기 지시어에서)는 public(기본값), protected 또는 private으로 한정될 수 있습니다. + +# 프로토타입 + +다른 객체와 _인스턴스화_ 또는 _특수화_ 관계가 없는 객체는 프로토타입 역할을 합니다. 프로토타입은 다른 객체, 즉 부모 프로토타입을 _확장_할 수 있습니다. + +```logtalk +% 우리의 프로토타입 코끼리, clyde +:- object(clyde). + + :- public(color/1). + color(grey). + + :- public(number_of_legs/1). + number_of_legs(4). + +:- end_object. + +% 또 다른 코끼리 fred는 clyde와 같지만 흰색입니다. +:- object(fred, + extends(clyde)). + + color(white). + +:- end_object. +``` + +프로토타입 역할을 하는 객체로 전송된 메시지에 응답할 때, 우리는 메시지를 확인하고 먼저 프로토타입 자체에서 답을 찾고, 찾지 못하면 부모 프로토타입에 위임합니다: + +```logtalk +?- fred::number_of_legs(N). +N = 4 +yes + +?- fred::color(C). +C = white +yes +``` + +해당 술어가 선언되고(송신자가 범위 내에 있는 경우) 메시지가 유효하지만, 술어가 정의되지 않은 경우 오류를 발생시키는 대신 실패합니다. 이것을 _폐쇄 세계 가정_이라고 합니다. 예를 들어, `foo.lgt` 파일에 저장된 다음 객체를 고려하십시오: + +```logtalk +:- object(foo). + + :- public(bar/0). + +:- end_object. +``` + +파일을 로드하고 `bar/0` 술어를 호출하려고 하면 예상대로 실패합니다. 이것은 _알려지지 않은_ 술어를 호출하는 것과는 다르며, 오류가 발생합니다: + +```logtalk +?- {foo}. +yes + +?- foo::bar. +no + +?- catch(foo::baz, Error, true). +Error = error( + existence_error(predicate_declaration, baz/0), + logtalk(foo::baz, user) +) +yes +``` + +# 클래스와 인스턴스 + +클래스 및/또는 인스턴스 역할을 하는 객체를 정의하려면, 객체는 다른 객체와 최소한 하나의 인스턴스화 또는 특수화 관계를 가져야 합니다. 클래스를 인스턴스로도 봐야 할 때 메타클래스 역할을 하는 객체를 사용할 수 있습니다. 다음 예제를 사용하여 런타임에 새 객체를 동적으로 생성하는 방법을 설명합니다: + +```logtalk +% 인스턴스에 대한 new/2 술어를 정의하는 간단하고 일반적인 메타클래스 +:- object(metaclass, + instantiates(metaclass)). + + :- public(new/2). + new(Instance, Clauses) :- + self(Class), + create_object(Instance, [instantiates(Class)], [], Clauses). + +:- end_object. + +% 인스턴스에 대한 age/1 및 name/1 술어를 정의하는 간단한 클래스 +:- object(person, + instantiates(metaclass)). + + :- public([ + age/1, name/1 + ]). + + % age/1의 기본값 + age(42). + +:- end_object. + +% person 클래스의 정적 인스턴스 +:- object(john, + instantiates(person)). + + name(john). + age(12). + +:- end_object. +``` + +인스턴스 역할을 하는 객체로 전송된 메시지에 응답할 때, 클래스에서 시작하여 필요한 경우 클래스 슈퍼클래스까지 올라가서 메시지를 확인합니다. 메시지가 유효하다고 가정하면, 인스턴스 자체에서 시작하여 답을 찾습니다: + +```logtalk +?- person::new(Instance, [name(paulo)]). +Instance = o1 +yes + +?- o1::name(Name). +Name = paulo +yes + +?- o1::age(Age). +Age = 42 +yes + +?- john::age(Age). +Age = 12 +yes +``` + +# 카테고리 + +카테고리는 모든 객체로 가져올 수 있는 _단일_ 기능을 구현하는 _응집력 있는_ 술어 선언 및 정의 집합을 캡슐화하는 데 사용되는 세분화된 코드 재사용 단위입니다. 따라서 카테고리는 프로토콜의 이중 개념으로 볼 수 있습니다. 다음 예에서는 자동차 엔진을 나타내는 카테고리를 정의한 다음 자동차 객체로 가져옵니다: + +```logtalk +% 엔진 특성을 설명하는 프로토콜 +:- protocol(carenginep). + + :- public([ + reference/1, + capacity/1, + cylinders/1, + horsepower_rpm/2, + bore_stroke/2, + fuel/1 + ]). + +:- end_protocol. + +% 카테고리로 정의된 일반적인 엔진 +:- category(classic, + implements(carenginep)). + + reference('M180.940'). + capacity(2195). + cylinders(6). + horsepower_rpm(94, 4800). + bore_stroke(80, 72.8). + fuel(gasoline). + +:- end_category. + +% 이전 엔진의 개조 버전 +:- category(sport, + extends(classic)). + + reference('M180.941'). + horsepower_rpm(HP, RPM) :- + ^^horsepower_rpm(ClassicHP, ClassicRPM), % "super" 호출 + HP is truncate(ClassicHP*1.23), + RPM is truncate(ClassicRPM*0.762). + +:- end_category. + +% 엔진(및 기타 구성 요소)으로 일부 자동차 "조립"을 시작할 수 있습니다. +:- object(sedan, + imports(classic)). + +:- end_object. + +:- object(coupe, + imports(sport)). + +:- end_object. +``` + +카테고리는 독립적으로 컴파일되므로 객체 재컴파일 없이 가져온 카테고리를 간단히 업데이트하여 가져오는 객체를 업데이트할 수 있습니다. 카테고리는 또한 _런타임 투명성_을 제공합니다. 즉, 카테고리 프로토콜은 카테고리를 가져오는 객체의 프로토콜에 추가됩니다: + +```logtalk +?- sedan::current_predicate(Predicate). +Predicate = reference/1 ; +Predicate = capacity/1 ; +Predicate = cylinders/1 ; +Predicate = horsepower_rpm/2 ; +Predicate = bore_stroke/2 ; +Predicate = fuel/1 +yes +``` + +# 핫 패치 + +카테고리는 객체를 핫 패치하는 데에도 사용할 수 있습니다. 카테고리는 객체에 새 술어를 추가하거나 객체 술어 정의를 대체할 수 있습니다. 예를 들어, 다음 객체를 고려하십시오: + +```logtalk +:- object(buggy). + + :- public(p/0). + p :- write(foo). + +:- end_object. +``` + +객체가 `p/0` 메시지를 받았을 때 잘못된 문자열을 인쇄한다고 가정합니다: + +```logtalk +?- {buggy}. +yes + +?- buggy::p. +foo +yes +``` + +객체 소스 코드를 사용할 수 없고 객체 코드를 실행하는 애플리케이션을 수정해야 하는 경우, 버그가 있는 술어를 수정하는 카테고리를 간단히 정의할 수 있습니다: + +```logtalk +:- category(patch, + complements(buggy)). + + % 수정된 p/0 정의 + p :- write(bar). + +:- end_category. +``` + +실행 중인 애플리케이션에 카테고리를 컴파일하고 로드한 후에는 다음을 얻게 됩니다: + +```logtalk +?- set_logtalk_flag(complements, allow). +yes + +?- {patch}. +yes + +?- buggy::p. +bar +yes +``` + +핫 패치는 캡슐화를 강제로 깨뜨리므로, `complements` 컴파일러 플래그를 (전역적으로 또는 객체별로) 설정하여 허용, 제한 또는 방지할 수 있습니다. + +# 매개변수 객체 및 카테고리 + +객체 및 카테고리는 식별자로 원자 대신 복합 항을 사용하여 매개변수화할 수 있습니다. 객체 및 카테고리 매개변수는 모든 캡슐화된 술어와 공유되는 _논리 변수_입니다. 기하학적 원에 대한 예: + +```logtalk +:- object(circle(_Radius, _Color)). + + :- public([ + area/1, perimeter/1 + ]). + + area(Area) :- + parameter(1, Radius), + Area is pi*Radius*Radius. + + perimeter(Perimeter) :- + parameter(1, Radius), + Perimeter is 2*pi*Radius. + +:- end_object. +``` + +매개변수 객체는 다른 객체와 마찬가지로 사용되며, 일반적으로 메시지를 보낼 때 매개변수 값을 제공합니다: + +```logtalk +?- circle(1.23, blue)::area(Area). +Area = 4.75291 +yes +``` + +매개변수 객체는 또한 술어 집합을 일반 Prolog 술어와 연결하는 간단한 방법을 제공합니다. Prolog 사실은 매개변수 객체의 식별자와 동일한 함자 및 인수를 가질 때 _매개변수 객체 프록시_로 해석될 수 있습니다. 프록시 작업을 위한 편리한 구문이 제공됩니다. 예를 들어, `circle/2` 술어에 대한 다음 절을 가정합니다: + +```logtalk +circle(1.23, blue). +circle(3.71, yellow). +circle(0.39, green). +circle(5.74, black). +circle(8.32, cyan). +``` + +이러한 절이 로드되면, 모든 원의 면적 목록을 쉽게 계산할 수 있습니다: + +```logtalk +?- findall(Area, {circle(_, _)}::area(Area), Areas). +Areas = [4.75291, 43.2412, 0.477836, 103.508, 217.468] +yes +``` + +`{Goal}::Message` 구문은 `Goal`을 증명하고(아마도 그 안의 변수를 인스턴스화함), 결과 항에 `Message`를 보냅니다. + +# 이벤트와 모니터 + +Logtalk는 이벤트와 해당 이벤트에 대한 모니터를 정의하여 _이벤트 기반 프로그래밍_을 지원합니다. 이벤트는 단순히 객체에 메시지를 보내는 것입니다. 메시지 보내기를 원자적 활동으로 해석하면 _before_ 이벤트와 _after_ 이벤트가 인식됩니다. 이벤트 모니터는 이벤트 핸들러 술어 `before/3` 및 `after/3`을 정의하고, 이벤트를 모니터와 연결하는 시스템 전체 이벤트 레지스트리를 쿼리, 등록 및 삭제할 수 있습니다. 예를 들어, `::/2` 제어 구문을 사용하여 전송되는 모든 메시지에 대한 간단한 추적기는 다음과 같이 정의할 수 있습니다: + +```logtalk +:- object(tracer, + implements(monitoring)). % 이벤트 핸들러를 위한 내장 프로토콜 + + :- initialization(define_events(_, _, _, _, tracer)). + + before(Object, Message, Sender) :- + write('call: '), writeq(Object), write(' <-- '), writeq(Message), + write(' from '), writeq(Sender), nl. + + after(Object, Message, Sender) :- + write('exit: '), writeq(Object), write(' <-- '), writeq(Message), + write(' from '), writeq(Sender), nl. + +:- end_object. +``` + +앞서 정의한 `tracer` 객체와 `list` 객체가 컴파일되고 로드되었다고 가정하면, 메시지를 보내 이벤트 핸들러의 동작을 관찰할 수 있습니다: + +```logtalk +?- set_logtalk_flag(events, allow). +yes + +?- list::member(X, [1,2,3]). + +call: list <-- member(X, [1,2,3]) from user +exit: list <-- member(1, [1,2,3]) from user +X = 1 ; +exit: list <-- member(2, [1,2,3]) from user +X = 2 ; +exit: list <-- member(3, [1,2,3]) from user +X = 3 +yes +``` + +`define_events/5` 및 `abolish_events/5` 내장 술어를 호출하여 런타임에 이벤트를 동적으로 설정하고 삭제할 수 있습니다. + +이벤트 기반 프로그래밍은 _계산적 리플렉션_의 한 형태로 볼 수 있습니다. 그러나 이벤트는 `::/2` 메시지 전송 제어 구문을 사용할 때만 생성된다는 점에 유의하십시오. + +# 람다 표현식 + +Logtalk는 람다 표현식을 지원합니다. 람다 매개변수는 `(>>)/2` 중위 연산자를 사용하여 람다에 연결된 목록을 사용하여 표현됩니다. 라이브러리 `meta`를 사용한 몇 가지 간단한 예: + +```logtalk +?- {meta(loader)}. +yes + +?- meta::map([X,Y]>>(Y is 2*X), [1,2,3], Ys). +Ys = [2,4,6] +yes +``` + +커링도 지원됩니다: + +```logtalk +?- meta::map([X]>>([Y]>>(Y is 2*X)), [1,2,3], Ys). +Ys = [2,4,6] +yes +``` + +람다 자유 변수는 확장된 구문 `{Free1, ...}/[Parameter1, ...]>>Lambda`를 사용하여 표현할 수 있습니다. + +# 매크로 + +소스 파일의 항과 목표는 텀 확장 및 목표 확장 규칙을 정의하는 _후크 객체_를 지정하여 컴파일 타임에 _확장_될 수 있습니다. 예를 들어, `source.lgt` 파일에 저장된 다음 간단한 객체를 고려하십시오: + +```logtalk +:- object(source). + + :- public(bar/1). + bar(X) :- foo(X). + + foo(a). foo(b). foo(c). + +:- end_object. +``` + +`foo/1` 로컬 술어에 대한 절과 호출을 확장하는 `my_macros.lgt` 파일에 저장된 다음 후크 객체를 가정합니다: + +```logtalk +:- object(my_macros, + implements(expanding)). % 술어 확장을 위한 내장 프로토콜 + + term_expansion(foo(Char), baz(Code)) :- + char_code(Char, Code). % 표준 내장 술어 + + goal_expansion(foo(X), baz(X)). + +:- end_object. +``` + +매크로 파일을 로드한 후, `hook` 컴파일러 플래그를 사용하여 소스 파일을 확장할 수 있습니다: + +```logtalk +?- logtalk_load(my_macros), logtalk_load(source, [hook(my_macros)]). +yes + +?- source::bar(X). +X = 97 ; +X = 98 ; +X = 99 +true +``` + +Logtalk 라이브러리는 다른 워크플로를 사용하여 후크 객체를 결합하는 지원을 제공합니다(예: 확장 파이프라인 정의). + +# 추가 정보 + +자세한 내용은 [Logtalk 웹사이트](http://logtalk.org)를 방문하십시오. diff --git a/ko/lolcode.md b/ko/lolcode.md new file mode 100644 index 0000000000..29fd6ff9ef --- /dev/null +++ b/ko/lolcode.md @@ -0,0 +1,186 @@ +--- +name: LOLCODE +filename: learnLOLCODE.lol +contributors: + - ["abactel", "https://github.com/abactel"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +LOLCODE는 [롤캣](https://upload.wikimedia.org/wikipedia/commons/a/ab/Lolcat_in_folder.jpg?1493656347257)의 말투를 닮도록 설계된 난해한 프로그래밍 언어입니다. + +``` +BTW 이건 한 줄 주석임 +BTW 모든 코드는 `HAI <언어 버전>`으로 시작해서 `KTHXBYE`로 끝나야 함 + +HAI 1.3 +CAN HAS STDIO? BTW 표준 헤더 가져오는 중 + +OBTW + ========================================================================== + ================================= 기초 =================================== + ========================================================================== +TLDR + +BTW 텍스트 표시하기: +VISIBLE "HELLO WORLD" + +BTW 변수 선언하기: +I HAS A MESSAGE ITZ "CATZ ARE GOOD" +VISIBLE MESSAGE + +OBTW + (이건 코드 블록임.) 변수는 동적 타입이라서 타입을 선언할 필요 없음. + 변수 타입은 내용에 따라 정해짐. 타입 종류는 다음과 같음: +TLDR + +I HAS A STRING ITZ "DOGZ ARE GOOOD" BTW 타입은 YARN (실) +I HAS A INTEGER ITZ 42 BTW 타입은 NUMBR (숫자) +I HAS A FLOAT ITZ 3.1415 BTW 타입은 NUMBAR (소수) +I HAS A BOOLEAN ITZ WIN BTW 타입은 TROOF (진실) +I HAS A UNTYPED BTW 타입은 NOOB (뉴비) + +BTW 사용자 입력 받기: +I HAS A AGE +GIMMEH AGE +BTW 변수는 YARN으로 저장됨. NUMBR로 바꾸려면: +AGE IS NOW A NUMBR + +OBTW + ========================================================================== + ================================== 수학 ================================== + ========================================================================== +TLDR + +BTW LOLCODE는 폴란드 표기법 스타일의 수학을 씀. + +BTW 기본 수학 표기법: + +SUM OF 21 AN 33 BTW 21 + 33 +DIFF OF 90 AN 10 BTW 90 - 10 +PRODUKT OF 12 AN 13 BTW 12 * 13 +QUOSHUNT OF 32 AN 43 BTW 32 / 43 +MOD OF 43 AN 64 BTW 43 modulo 64 +BIGGR OF 23 AN 53 BTW max(23, 53) +SMALLR OF 53 AN 45 BTW min(53, 45) + +BTW 2진 표기법: + +BOTH OF WIN AN WIN BTW 그리고: x=WIN, y=WIN이면 WIN +EITHER OF FAIL AN WIN BTW 또는: x=FAIL, y=FAIL이면 FAIL +WON OF WIN AN FAIL BTW xor: x=y이면 FAIL +NOT FAIL BTW 단항 부정: x=FAIL이면 WIN +ALL OF WIN AN WIN MKAY BTW 무한 인수 AND +ANY OF WIN AN FAIL MKAY BTW 무한 인수 OR + +BTW 비교: + +BOTH SAEM "CAT" AN "DOG" BTW x == y 이면 WIN +DIFFRINT 732 AN 184 BTW x != y 이면 WIN +BOTH SAEM 12 AN BIGGR OF 12 AN 4 BTW x >= y +BOTH SAEM 43 AN SMALLR OF 43 AN 56 BTW x <= y +DIFFRINT 64 AN SMALLR OF 64 AN 2 BTW x > y +DIFFRINT 75 AN BIGGR OF 75 AN 643 BTW x < y + +OBTW + ========================================================================== + ============================== 흐름 제어 ============================== + ========================================================================== +TLDR + +BTW If/then 문: +I HAS A ANIMAL +GIMMEH ANIMAL +BOTH SAEM ANIMAL AN "CAT", O RLY? + YA RLY + VISIBLE "고양이가 있군요" + MEBBE BOTH SAEM ANIMAL AN "MAUS" + VISIBLE "냠냠냠. 내가 먹었음." + NO WAI + VISIBLE "으악 멍멍이다" +OIC + +BTW Case 문: +I HAS A COLOR +GIMMEH COLOR +COLOR, WTF? + OMG "R" + VISIBLE "빨간 물고기" + GTFO + OMG "Y" + VISIBLE "노란 물고기" + BTW `GTFO`가 없어서 다음 문장도 테스트될 거임 + OMG "G" + OMG "B" + VISIBLE "물고기는 맛이 있음" + GTFO + OMGWTF + VISIBLE "물고기가 투명해 오노 왓" +OIC + +BTW For 반복문: +I HAS A TEMPERATURE +GIMMEH TEMPERATURE +TEMPERATURE IS NOW A NUMBR +IM IN YR LOOP UPPIN YR ITERATOR TIL BOTH SAEM ITERATOR AN TEMPERATURE + VISIBLE ITERATOR +IM OUTTA YR LOOP + +BTW While 반복문: +IM IN YR LOOP NERFIN YR ITERATOR WILE DIFFRINT ITERATOR AN -10 + VISIBLE ITERATOR +IM OUTTA YR LOOP + +OBTW + ========================================================================= + =============================== 문자열 ================================ + ========================================================================= +TLDR + +BTW 줄바꿈: +VISIBLE "첫 번째 줄 :) 두 번째 줄" + +BTW 탭: +VISIBLE ":>공백이 최고임" + +BTW 벨 (삑 소리 남): +VISIBLE "다음 손님 오세요 :o" + +BTW 리터럴 큰따옴표: +VISIBLE "그가 말했음 :"나는 케이크를 좋아해:"" + +BTW 리터럴 콜론: +VISIBLE "내가 사는 곳:: 사이버 공간" + +OBTW + ========================================================================= + =============================== 함수 =============================== + ========================================================================= +TLDR + +BTW 새 함수 선언하기: +HOW IZ I SELECTMOVE YR MOVE BTW `MOVE`는 인수임 + BOTH SAEM MOVE AN "ROCK", O RLY? + YA RLY + VISIBLE "바위가 있군요" + NO WAI + VISIBLE "오노 가위-가위다" + OIC + GTFO BTW 이건 NOOB를 반환함 +IF U SAY SO + +BTW 함수 선언하고 값 반환하기: +HOW IZ I IZYELLOW + FOUND YR "YELLOW" +IF U SAY SO + +BTW 함수 호출하기: +I IZ IZYELLOW MKAY + +KTHXBYE +``` + +## 더 읽을거리: + +- [LCI 컴파일러](https://github.com/justinmeza/lci) +- [공식 사양](https://github.com/justinmeza/lolcode-spec/blob/master/v1.2/lolcode-spec-v1.2.md) diff --git a/ko/m.md b/ko/m.md new file mode 100644 index 0000000000..aa0edf76f4 --- /dev/null +++ b/ko/m.md @@ -0,0 +1,397 @@ +--- +name: M (MUMPS) +contributors: + - ["Fred Turkington", "http://z3ugma.github.io"] +filename: LEARNM.m +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +M 또는 MUMPS(Massachusetts General Hospital Utility Multi-Programming System)는 내장 NoSQL 데이터베이스가 있는 절차적 언어입니다. 또는 데이터베이스에 접근하고 조작하는 데 최적화된 통합 언어가 있는 데이터베이스입니다. M의 주요 특징은 메모리의 지역 변수와 영구 저장소에 접근하는 데 동일한 기본 구문을 사용하므로 별도의 쿼리 언어를 기억할 필요가 없다는 것입니다. 이로 인해 특히 초보자에게 프로그래밍이 빠릅니다. M의 구문은 컴퓨터 메모리가 비싸고 제한적이었던 시대에 간결하게 설계되었습니다. 이 간결한 스타일은 스크롤 없이 한 화면에 더 많은 내용을 담을 수 있다는 것을 의미합니다. + +M 데이터베이스는 높은 처리량의 트랜잭션 처리를 위해 설계된 계층적 키-값 저장소입니다. 데이터베이스는 "글로벌"(전역 변수용)이라는 트리 구조로 구성되며, 이는 JSON과 같은 최신 형식과 유사한 희소 데이터 구조입니다. + +원래 1966년 의료 애플리케이션을 위해 설계된 M은 의료 시스템 및 금융 기관에서 높은 처리량의 실시간 애플리케이션을 위해 계속 널리 사용되고 있습니다. + +### 예제 + +다음은 확장된 구문을 사용하여 피보나치 수열을 계산하는 M 프로그램의 예입니다: + +``` +fib ; 첫 몇 개의 피보나치 항 계산 + new i,a,b,sum + set (a,b)=1 ; 초기 조건 + for i=1:1 do quit:sum>1000 + . set sum=a+b + . write !,sum + . set a=b,b=sum +``` + +### 주석 +M의 주석은 세미콜론 주석 마커 앞에 최소 한 칸의 공백이 필요합니다. +최소 두 개의 세미콜론(;;)으로 시작하는 주석은 실행 중인 프로그램 내에서 접근할 수 있음이 보장됩니다. + +``` + ; 주석은 세미콜론(;)으로 시작합니다. +``` + +### 데이터 타입 + +M에는 하나의 데이터 타입(문자열)과 해당 문자열에 대한 세 가지 해석이 있습니다.: + +``` +; 문자열 - 큰따옴표로 묶인 문자. +; ""는 null 문자열입니다. 문자열 내에서 "를 사용하려면 ""를 사용하십시오. +; 예: "hello", "Scrooge said, ""Bah, Humbug!""" +; +; 숫자 - 쉼표 없음, 앞뒤 0 제거. +; 'E'를 사용한 과학적 표기법 ('e'가 아님). +; 최소 IEEE 754 배정밀도 값을 가짐(15자리 정밀도 보장). +; 예: 20 (20으로 저장됨), 1e3 (1000으로 저장됨), 0500.20 (500.2로 저장됨), +; 2020년 10월 12일 http://www.usdebt.org에서 검색한 미국 국가 부채는 27041423576201.15입니다) +; (최소 27041422576201.10으로 저장되어야 하지만 대부분의 구현에서는 27041432576201.15로 저장합니다) +; +; 진리값 - 0으로 해석되는 문자열은 false로 사용되고, 0이 아닌 값(예: 1)으로 해석되는 모든 문자열은 true로 사용됩니다. +``` + +### 명령어 + +명령어는 대소문자를 구분하지 않으며, 전체 형식과 종종 첫 글자로 된 축약형이 있습니다. 명령어는 명령어에 따라 0개 이상의 인수를 가집니다. 이 페이지에는 이 간결한 구문으로 작성된 프로그램이 포함되어 있습니다. M은 공백을 인식합니다. 공백은 명령어와 인수 사이의 구분 기호로 처리됩니다. 각 명령어는 인수와 1개의 공백으로 구분됩니다. 인수가 없는 명령어 뒤에는 2개의 공백이 옵니다. (기술적으로 이것을 인수 없는 명령어라고 합니다) + +#### 모든 국내 및 국제 M 표준의 공통 명령어 + +#### Write (W로 축약) + +현재 장치에 데이터 인쇄. + +``` +WRITE !,"hello world" +``` + +출력 서식 문자: + + ! 문자는 새 줄에 대한 구문입니다. + # 문자는 새 페이지에 대한 구문입니다. + ? 문자와 숫자 표현식의 시퀀스는 해당 숫자 열이 인쇄될 때까지 공백을 출력하는 구문입니다. + +다음 명령어로 가기 전 공백 구분자 앞에 여러 문장을 추가 인수로 제공할 수 있습니다: + +``` +w !,"foo bar"," ","baz" +``` + +#### Read (R로 축약) + +사용자로부터 입력 검색 + +``` +READ var +r !,"로미오, 왜 그대는 로미오인가요? ",why +``` + +모든 M 명령어와 마찬가지로 read 명령어에도 여러 인수를 전달할 수 있습니다. 따옴표로 묶인 문자열, 숫자, 서식 문자와 같은 상수는 직접 출력됩니다. 전역 변수와 지역 변수의 값은 모두 사용자로부터 검색됩니다. 터미널은 사용자가 첫 번째 변수를 입력할 때까지 기다렸다가 두 번째 프롬프트를 표시합니다. + +``` +r !,"첫 번째가 낫나요, 두 번째가 낫나요? ",lorem," 두 번째가 낫나요, 세 번째가 낫나요? ",ipsum +``` + +#### Set (S로 축약) + +변수에 값 할당 + +``` +SET name="Benjamin Franklin" +s centi=0.01,micro=10E-6 +w !,centi,!,micro + +;.01 +;.00001 +``` + +#### Kill (K로 축약) + +메모리에서 변수를 제거하거나 디스크에서 데이터베이스 항목을 제거합니다. +데이터베이스 노드(전역 변수)는 변수 이름 앞에 캐럿 문자(^)가 붙는지 여부에 따라 제거됩니다. +그렇지 않으면 지역 변수가 메모리에서 제거됩니다. +KILL되면 자동 가비지 수집이 발생합니다. + +``` +KILL centi +k micro +``` + +### 전역 변수와 배열 + +지역 변수 외에도 M에는 M의 내장 데이터베이스인 영구 공유 변수가 있습니다. 이들은 디스크에 저장되며 _글로벌_이라고 합니다. 전역 이름은 반드시 __캐럿__(__^__)으로 시작해야 합니다. + +모든 변수(지역 또는 전역)는 _아래 첨자_를 할당하여 배열이 될 수 있습니다. 배열은 희소하며 미리 정의된 크기가 없습니다. 데이터가 저장된 경우에만 값이 메모리를 사용합니다. 배열은 아래 첨자가 분기이고 할당된 값이 잎인 트리처럼 시각화해야 합니다. 배열의 모든 노드에 값이 있어야 하는 것은 아닙니다. + +``` +s ^cars=20 +s ^cars("Tesla",1,"Name")="Model 3" +s ^cars("Tesla",2,"Name")="Model X" +s ^cars("Tesla",2,"Doors")=5 + +w !,^cars +; 20 +w !,^cars("Tesla") +; null 값 - 이 노드에는 값이 할당되지 않았지만 자식이 있습니다. +w !,^cars("Tesla",1,"Name") +; Model 3 +``` + +배열의 인덱스 값은 자동으로 정렬됩니다. "MUMPS는 정렬한다고 말할 필요가 없다는 것을 의미한다"는 캐치프레이즈가 있습니다. 관심 있는 값을 값 대신 배열의 마지막 자식 아래 첨자로 설정한 다음 해당 노드에 빈 문자열을 저장하여 내장 정렬을 활용하십시오. + +``` +; 날짜 및 시간별 온도 기록 +s ^TEMPS("11/12","0600",32)="" +s ^TEMPS("11/12","1030",48)="" +s ^TEMPS("11/12","1400",49)="" +s ^TEMPS("11/12","1700",43)="" +``` + +### 연산자 + +``` +; 할당: = +; 단항: + 문자열 값을 숫자 값으로 변환합니다. +; 산술: +; + 덧셈 +; - 뺄셈 +; * 곱셈 +; / 부동 소수점 나눗셈 +; \ 정수 나눗셈 +; # 모듈로 +; ** 거듭제곱 +; 논리: +; & and +; ! or +; ' not +; 비교: +; = 같음 +; '= 같지 않음 +; > 보다 큼 +; < 보다 작음 +; '> 크지 않음 / 작거나 같음 +; '< 작지 않음 / 크거나 같음 +; 문자열 연산자: +; _ 연결 +; [ 포함 a는 b를 포함 +; ]] 정렬 후 a는 b 뒤에 옴 +; '[ 포함하지 않음 +; ']] 정렬 후가 아님 +``` + +#### 연산 순서 + +M의 연산은 _엄격하게_ 왼쪽에서 오른쪽으로 평가됩니다. 다른 연산자보다 우선 순위가 높은 연산자는 없습니다. +예를 들어, 덧셈보다 곱셈이 먼저 평가되는 연산 순서는 없습니다. +이 순서를 변경하려면 괄호를 사용하여 먼저 평가할 표현식을 그룹화하면 됩니다. + +``` +w 5+3*20 +;160 +;아마 65를 원했을 것입니다. +write 5+(3*20) +``` + +### 흐름 제어, 블록 및 코드 구조 + +단일 M 파일을 _루틴_이라고 합니다. 주어진 루틴 내에서 코드를 _태그_를 사용하여 더 작은 덩어리로 나눌 수 있습니다. 태그는 1열에서 시작하고 해당 태그에 관련된 명령어는 들여쓰기됩니다. + +태그는 매개변수를 받고 값을 반환할 수 있으며, 이것이 함수입니다. 함수는 '$$'로 호출됩니다: + +``` +; 'tag' 함수를 실행하고, 두 개의 매개변수를 가지며, 결과를 씁니다. +w !,$$tag^routine(a,b) +``` + +M에는 실행 스택이 있습니다. 스택의 모든 수준이 반환되면 프로그램이 종료됩니다. 수준은 _do_ 명령어로 스택에 추가되고 _quit_ 명령어로 제거됩니다. + +#### Do (D로 축약) + +인수 사용: 코드 블록을 실행하고 스택에 수준을 추가합니다. + +``` +d ^routine ;루틴을 처음부터 실행합니다. +; ;루틴은 캐럿으로 식별됩니다. +d tag ;현재 루틴에서 태그를 실행합니다. +d tag^routine ;다른 루틴에서 태그를 실행합니다. +``` + +인수 없는 do: 코드 블록을 만드는 데 사용됩니다. 블록은 블록의 각 수준에 대해 마침표로 들여쓰기됩니다: + +``` +set a=1 +if a=1 do +. write !,a +. read b +. if b > 10 d +. . w !, b +w "hello" +``` + +#### Quit (Q로 축약) +이 블록 실행을 중지하고 이전 스택 수준으로 돌아갑니다. +Quit는 명령어 뒤에 단일 공백을 붙여 값을 반환할 수 있습니다. +Quit는 루프를 중지할 수 있습니다. 두 개의 공백을 붙이는 것을 잊지 마십시오. +루프 외부의 Quit는 현재 서브루틴에서 반환되며 두 개의 공백이나 개행 문자가 뒤따릅니다. + +#### New (N로 축약) +주어진 변수의 값을 _이 스택 수준에서만_ 지워진 값으로 숨깁니다. 부작용을 방지하는 데 유용합니다. + +이 모든 것을 종합하여 M 루틴의 전체 예를 만들 수 있습니다: + +``` +; RECTANGLE - 사각형 수학을 다루는 루틴 + q ; 특정 태그가 호출되지 않으면 종료 + +main + n length,width ; 이전 값이 유지되지 않도록 새 길이와 너비 + w !,"RECTANGLE에 오신 것을 환영합니다. 사각형의 치수를 입력하십시오." + r !,"길이? ",length,!,"너비? ",width + d area(length,width) ;태그를 사용하여 서브루틴 호출/실행 + s per=$$perimeter(length,width) ;함수 값 가져오기 + w !,"둘레: ",per + quit + +area(length,width) ; 매개변수를 받는 태그입니다. + ; 값이 없이 종료되므로 함수가 아닙니다. + w !, "면적: ",length*width + q ; 종료: 스택의 이전 수준으로 돌아갑니다. + +perimeter(length,width) + q 2*(length+width) ; Quit를 사용하여 값을 반환합니다. ; 이것은 함수입니다. +``` + + + +### 조건문, 반복문 및 $Order() + +F(or) 루프는 몇 가지 다른 패턴을 따를 수 있습니다: + +```jinja +;카운터가 있는 유한 루프 +;f var=start:increment:stop + +f i=0:5:25 w i," " +;0 5 10 15 20 25 + +; 카운터가 있는 무한 루프 +; 카운터는 영원히 증가합니다. 루프를 빠져나가려면 조건부 Quit를 사용하십시오. +;f var=start:increment + +f j=1:1 w j," " i j>1E3 q +; 공백으로 구분된 1-1000 인쇄 + +;인수 없는 for - 무한 루프. 조건부 Quit를 사용하십시오. +; "영원히"로도 읽습니다 - f 또는 for 뒤에 두 개의 공백. +s var="" +f s var=var_"%" w !,var i var="%%%%%%%%%%" q +; % +; %% +; %%% +; %%%% +; %%%%% +; %%%%%% +; %%%%%%% +; %%%%%%%% +; %%%%%%%%% +; %%%%%%%%%% +``` + +#### I(f), E(lse), 후조건 + +M에는 조건부 평가를 위한 if/else 구조가 있지만, 모든 명령어는 _후조건_을 사용하여 추가 if 문 없이 조건부로 실행될 수 있습니다. 이것은 명령어 바로 뒤에 콜론(:)으로 구분되어 나타나는 조건입니다. + +```jinja +;전통적인 if/else를 사용한 조건문 +r "숫자 입력: ",num +i num>100 w !,"엄청 큼" +e i num>10 w !,"큼" +e w !,"작음" + +; 후조건은 for 루프에서 특히 유용합니다. +; 이것이 지배적인 for 루프 구조입니다: +; 'for' 문 +; 후조건으로 'quit' 조건을 테스트하는 +; 그런 다음 각 반복에 대해 들여쓰기된 블록을 'do'합니다. + +s var="" +f s var=var_"%" q:var="%%%%%%%%%%" d ;"var가 "%%%%%%%%%%"이면 종료"로 읽습니다. +. w !,var + +;보너스 포인트 - $L(ength) 내장 함수를 사용하면 훨씬 더 간결해집니다. + +s var="" +f s var=var_"%" q:$L(var)>10 d ; +. w !,var +``` + +#### 배열 반복 - $Order +이전 예에서 보았듯이 M에는 $$로 호출되는 사용자 정의 함수와 비교하여 단일 $로 호출되는 내장 함수가 있습니다. 이 함수들은 명령어처럼 축약형이 있습니다. +가장 유용한 것 중 하나는 __$Order()__ / $O()입니다. 배열 아래 첨자를 주면 $O는 해당 배열의 다음 아래 첨자를 반환합니다. 마지막 아래 첨자에 도달하면 ""를 반환합니다. + +```jinja +;이전의 ^TEMPS 전역 변수를 다시 호출해 봅시다: +; 날짜 및 시간별 온도 기록 +s ^TEMPS("11/12","0600",32)="" +s ^TEMPS("11/12","0600",48)="" +s ^TEMPS("11/12","1400",49)="" +s ^TEMPS("11/12","1700",43)="" +; 일부 추가 +s ^TEMPS("11/16","0300",27)="" +s ^TEMPS("11/16","1130",32)="" +s ^TEMPS("11/16","1300",47)="" + +;온도가 있는 모든 날짜를 인쇄하는 루프입니다: +n date,time ; 이 변수들을 ""로 초기화합니다. + +; 이 줄은 다음과 같이 읽습니다: 영원히; date를 ^TEMPS의 다음 날짜로 설정합니다. +; date가 ""로 설정되면 끝에 도달했음을 의미하므로 종료합니다. +; 아래 블록을 실행합니다. +f s date=$ORDER(^TEMPS(date)) q:date="" d +. w !,date + +; 시간도 추가합니다: +f s date=$ORDER(^TEMPS(date)) q:date="" d +. w !,"날짜: ",date +. f s time=$O(^TEMPS(date,time)) q:time="" d +. . w !,"시간: ",time + +; 온도를 기준으로 먼저 정렬하는 인덱스를 만듭니다 - +; 특정 온도였던 날짜와 시간은? +n date,time,temp +f s date=$ORDER(^TEMPS(date)) q:date="" d +. f s time=$O(^TEMPS(date,time)) q:time="" d +. . f s temp=$O(^TEMPS(date,time,temp)) q:temp="" d +. . . s ^TEMPINDEX(temp,date,time)="" + +;이것은 다음과 같은 전역 변수를 생성합니다. +^TEMPINDEX(27,"11/16","0300") +^TEMPINDEX(32,"11/12","0600") +^TEMPINDEX(32,"11/16","1130") +``` + +## 더 읽을거리 + +M에 대해 배울 것이 훨씬 더 많습니다. 북부 아이오와 대학의 Kevin O'Kane 교수의 [MUMPS 언어 소개][1] 프레젠테이션에서 훌륭한 짧은 튜토리얼을 제공합니다. VistA를 사용한 M에 대한 자세한 내용은 + +Intersystems에는 M 프로그래밍 언어의 상위 집합인 일부 제품이 있습니다. + +* [Iris 설명 페이지][5] +* [Cache 설명 페이지][6] + +컴퓨터에 M 인터프리터 / 데이터베이스를 설치하려면 [YottaDB Docker 이미지][2]를 사용해 보십시오. + +YottaDB와 그 전신인 GT.M은 데이터베이스 트랜잭션, 잠금 및 복제를 포함한 모든 언어 기능에 대한 철저한 문서를 가지고 있습니다: + +* [YottaDB 프로그래머 가이드][3] +* [GT.M 프로그래머 가이드][4] + +[1]: https://www.cs.uni.edu/~okane/source/MUMPS-MDH/MumpsTutorial.pdf +[2]: https://yottadb.com/product/get-started/ +[3]: https://docs.yottadb.com/ProgrammersGuide/langfeat.html +[4]: http://tinco.pair.com/bhaskar/gtm/doc/books/pg/UNIX_manual/index.html +[5]: https://www.intersystems.com/products/intersystems-iris/ +[6]: https://en.wikipedia.org/wiki/InterSystems_Caché diff --git a/ko/make.md b/ko/make.md new file mode 100644 index 0000000000..5c51f8fb68 --- /dev/null +++ b/ko/make.md @@ -0,0 +1,245 @@ +--- +category: tool +name: Make +contributors: + - ["Robert Steed", "https://github.com/robochat"] + - ["Stephan Fuhrmann", "https://github.com/sfuhrm"] +filename: Makefile +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Makefile은 대상(또는 대상들)을 생성하기 위한 규칙 그래프를 정의합니다. +그 목적은 대상을 소스의 최신 버전으로 업데이트하는 데 필요한 최소한의 작업을 +수행하는 것입니다. 1976년 Stuart Feldman이 주말 동안 작성한 것으로 유명하며, +많은 경쟁자와 비판에도 불구하고 (특히 Unix 및 Linux에서) 여전히 널리 사용됩니다. + +존재하는 make에는 여러 종류가 있지만, 이 글에서는 Linux의 표준인 +GNU make를 사용한다고 가정합니다. + +```make +# 주석은 이렇게 작성할 수 있습니다. + +# 파일 이름은 Makefile이어야 하며, `make `으로 실행할 수 있습니다. +# 그렇지 않으면 `make -f "filename" `을 사용합니다. + +# 경고 - Makefile에서는 들여쓰기에 탭만 사용하고 공백은 절대 사용하지 마십시오! + +#----------------------------------------------------------------------- +# 기본 +#----------------------------------------------------------------------- + +# 규칙은 다음과 같은 형식입니다. +# target: +# 여기서 prerequisite는 선택 사항입니다. + +# 규칙 - 이 규칙은 file0.txt가 존재하지 않을 경우에만 실행됩니다. +file0.txt: + echo "foo" > file0.txt + # 이 '레시피' 섹션의 주석도 셸로 전달됩니다. + # `make file0.txt` 또는 단순히 `make`를 시도해보십시오 - 첫 번째 규칙이 기본값입니다. + +# 이 규칙은 file0.txt가 file1.txt보다 최신일 경우에만 실행됩니다. +file1.txt: file0.txt + cat file0.txt > file1.txt + # 셸에서와 동일한 인용 규칙을 사용합니다. + @cat file0.txt >> file1.txt + # @는 명령이 stdout에 에코되는 것을 방지합니다. + -@echo 'hello' + # -는 오류가 발생하더라도 make가 계속 진행됨을 의미합니다. + # 명령줄에서 `make file1.txt`를 시도해보십시오. + +# 규칙은 여러 대상과 여러 전제 조건을 가질 수 있습니다. +file2.txt file3.txt: file0.txt file1.txt + touch file2.txt + touch file3.txt + +# make는 동일한 규칙에 대한 여러 레시피에 대해 불평합니다. +# 하지만 빈 레시피는 계산되지 않으며 새 종속성을 추가하는 데 사용할 수 있습니다. + +#----------------------------------------------------------------------- +# 포니 타겟 +#----------------------------------------------------------------------- + +# 포니 타겟. 파일이 아닌 모든 타겟. +# 절대 최신 상태가 아니므로 make는 항상 실행하려고 시도합니다. +all: maker process + +# 순서에 상관없이 선언할 수 있습니다. +maker: + touch ex0.txt ex1.txt + +# 실제 파일과 이름이 같을 때 포니 규칙이 깨지는 것을 방지할 수 있습니다. +.PHONY: all maker process +# 이것은 특별한 타겟입니다. 다른 여러 가지가 있습니다. + +# 포니 타겟에 대한 종속성이 있는 규칙은 항상 실행됩니다. +ex0.txt ex1.txt: maker + +# 일반적인 포니 타겟은 all make clean install ... 입니다. + +#----------------------------------------------------------------------- +# 자동 변수 및 와일드카드 +#----------------------------------------------------------------------- + +process: file*.txt #파일 이름과 일치하도록 와일드카드 사용 + @echo $^ # $^는 전제 조건 목록을 포함하는 변수입니다. + @echo $@ # 타겟 이름을 출력합니다. + #(여러 타겟 규칙의 경우, $@는 규칙을 실행하게 한 것입니다.) + @echo $< # 나열된 첫 번째 전제 조건 + @echo $? # 최신이 아닌 종속성만 + @echo $+ # 중복을 포함한 모든 종속성 (일반과 다름) + #@echo $| # 모든 '순서 전용' 전제 조건 + +# 규칙 종속성 정의를 분리하더라도 $^는 그것들을 찾습니다. +process: ex1.txt file0.txt +# ex1.txt는 발견되지만 file0.txt는 중복 제거됩니다. + +#----------------------------------------------------------------------- +# 패턴 +#----------------------------------------------------------------------- + +# 특정 파일을 다른 파일로 변환하는 방법을 make에 가르칠 수 있습니다. + +%.png: %.svg + inkscape --export-png $^ + +# 패턴 규칙은 make가 타겟을 생성하기로 결정한 경우에만 작동합니다. + +# 디렉토리 경로는 일반적으로 패턴 규칙을 일치시킬 때 무시됩니다. 하지만 +# make는 사용 가능한 가장 적절한 규칙을 사용하려고 시도합니다. +small/%.png: %.svg + inkscape --export-png --export-dpi 30 $^ + +# make는 찾은 패턴 규칙의 마지막 버전을 사용합니다. +%.png: %.svg + @echo this rule is chosen + +# 그러나 make는 타겟을 만들 수 있는 첫 번째 패턴 규칙을 사용합니다. +%.png: %.ps + @echo this rule is not chosen if *.svg and *.ps are both present + +# make에는 이미 일부 패턴 규칙이 내장되어 있습니다. 예를 들어, +# *.c 파일을 *.o 파일로 변환하는 방법을 알고 있습니다. + +# 이전 Makefile은 패턴 규칙 대신 접미사 규칙을 사용할 수 있습니다. +.png.ps: + @echo this rule is similar to a pattern rule. + +# make에 접미사 규칙에 대해 알립니다. +.SUFFIXES: .png + +#----------------------------------------------------------------------- +# 변수 +#----------------------------------------------------------------------- +# aka. 매크로 + +# 변수는 기본적으로 모두 문자열 타입입니다. + +name = Ted +name2="Sarah" + +echo: + @echo $(name) + @echo ${name2} + @echo $name # 이것은 작동하지 않으며, $(n)ame으로 처리됩니다. + @echo $(name3) # 알 수 없는 변수는 빈 문자열로 처리됩니다. + +# 변수를 설정하는 4가지 방법이 있습니다. +# 우선 순위가 높은 순서대로: +# 1: 명령줄 인수 +# 2: Makefile +# 3: 셸 환경 변수 - make는 이것들을 자동으로 가져옵니다. +# 4: make에는 일부 미리 정의된 변수가 있습니다. + +name4 ?= Jean +# 환경 변수가 아직 정의되지 않은 경우에만 변수를 설정합니다. + +override name5 = David +# 명령줄 인수가 이 변수를 변경하는 것을 중지합니다. + +name4 +=grey +# 변수에 값 추가 (공백 포함). + +# 패턴별 변수 값 (GNU 확장). +echo: name2 = Sara # 일치하는 규칙 내에서 참 + # 그리고 재귀적으로 재구성된 종속성 내에서도 + # (그래프가 너무 복잡해지면 깨질 수 있음!) + +# make에 의해 자동으로 정의된 일부 변수. +echo_inbuilt: + echo $(CC) + echo ${CXX} + echo $(FC) + echo ${CFLAGS} + echo $(CPPFLAGS) + echo ${CXXFLAGS} + echo $(LDFLAGS) + echo ${LDLIBS} + +#----------------------------------------------------------------------- +# 변수 2 +#----------------------------------------------------------------------- + +# 첫 번째 유형의 변수는 사용될 때마다 평가됩니다. +# 이것은 비용이 많이 들 수 있으므로 한 번만 평가되는 두 번째 유형의 +# 변수가 존재합니다. (이것은 GNU make 확장입니다) + +var := hello +var2 ::= $(var) hello +#:=와 ::=는 동일합니다. + +# 이러한 변수는 절차적으로 평가되므로 (나타나는 순서대로), +# 언어의 나머지 부분과 충돌합니다! + +# 이것은 작동하지 않습니다. +var3 ::= $(var4) and good luck +var4 ::= good night + +#----------------------------------------------------------------------- +# 함수 +#----------------------------------------------------------------------- + +# make에는 많은 함수가 있습니다. + +sourcefiles = $(wildcard *.c */*.c) +objectfiles = $(patsubst %.c,%.o,$(sourcefiles)) + +# 형식은 $(func arg0,arg1,arg2...)입니다. + +# 일부 예제 +ls: * src/* + @echo $(filter %.txt, $^) + @echo $(notdir $^) + @echo $(join $(dir $^),$(notdir $^)) + +#----------------------------------------------------------------------- +# 지시문 +#----------------------------------------------------------------------- + +# 다른 makefile 포함, 플랫폼별 코드에 유용합니다. +include foo.mk + +sport = tennis +# 조건부 컴파일 +report: +ifeq ($(sport),tennis) + @echo 'game, set, match' +else + @echo "They think it's all over; it is now" +endif + +# ifneq, ifdef, ifndef도 있습니다. + +foo = true + +ifdef $(foo) +bar = 'hello' +endif +``` + +### 더 많은 자료 + +- [GNU Make 문서](https://www.gnu.org/software/make/manual/make.html) +- [Software Carpentry 튜토리얼](https://swcarpentry.github.io/make-novice/) +- [예제로 배우는 Makefile 튜토리얼](https://makefiletutorial.com/#makefile-cookbook) diff --git a/ko/markdown.md b/ko/markdown.md index c9dbe84e56..9354e9738e 100644 --- a/ko/markdown.md +++ b/ko/markdown.md @@ -2,8 +2,9 @@ contributors: - ["Dan Turkel", "http://danturkel.com/"] - ["Jacob Ward", "http://github.com/JacobCWard/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] --- - 마크다운은 2004년에 존 그루버가 창시했습니다. HTML으로 (그리고 이제는 다른 다양한 형식으로도) 쉽게 변환되는 읽고 쓰기 쉬운 문법입니다. 마크다운은 또한 파서마다 구현이 다양합니다. 본 문서는 어떤 기능이 보편적인지, diff --git a/ko/matlab.md b/ko/matlab.md new file mode 100644 index 0000000000..31704bbfff --- /dev/null +++ b/ko/matlab.md @@ -0,0 +1,560 @@ +--- +name: MATLAB +filename: learnmatlab.m +contributors: + - ["mendozao", "http://github.com/mendozao"] + - ["jamesscottbrown", "http://jamesscottbrown.com"] + - ["Colton Kohnke", "http://github.com/voltnor"] + - ["Claudson Martins", "http://github.com/claudsonm"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +MATLAB은 MATrix LABoratory의 약자입니다. 공학 및 수학에서 일반적으로 사용되는 강력한 수치 계산 언어입니다. + +```matlab +%% 코드 섹션은 두 개의 퍼센트 기호로 시작합니다. 섹션 제목은 같은 줄에 씁니다. +% 주석은 퍼센트 기호로 시작합니다. + +%{ +여러 줄 주석은 +다음과 +같습니다 +%} + +% 두 개의 퍼센트 기호는 새 코드 섹션의 시작을 나타냅니다 +% 개별 코드 섹션은 커서를 섹션으로 이동한 다음 +% "섹션 실행" 버튼을 클릭하거나 +% 또는 Ctrl+Shift+Enter(Windows) 또는 Cmd+Shift+Return(macOS)을 사용하여 실행할 수 있습니다. + +%% 이것은 코드 섹션의 시작입니다 +% 섹션을 사용하는 한 가지 방법은 데이터 로드와 같이 비용이 많이 들지만 변경되지 않는 시작 코드를 분리하는 것입니다. +load myFile.mat y + +%% 이것은 또 다른 코드 섹션입니다 +% 이 섹션은 자체적으로 반복적으로 편집하고 실행할 수 있으며, 탐색적 프로그래밍 및 데모에 유용합니다. +A = A * 2; +plot(A); + +%% 코드 섹션은 코드 셀 또는 셀 모드라고도 합니다(셀 배열과 혼동하지 마십시오). + + +% 명령어는 '...'을 사용하여 여러 줄에 걸쳐 작성할 수 있습니다: + a = 1 + 2 + ... + + 4 + +% 명령어를 운영 체제에 전달할 수 있습니다 +!ping google.com + +who % 메모리의 모든 변수를 표시합니다 +whos % 메모리의 모든 변수를 유형과 함께 표시합니다 +clear % 메모리에서 모든 변수를 지웁니다 +clear('A') % 특정 변수를 지웁니다 +openvar('A') % 변수 편집기에서 변수를 엽니다 + +clc % 명령 창의 내용을 지웁니다 +diary % 명령 창 텍스트를 파일에 쓰는 것을 토글합니다 +ctrl-c % 현재 계산을 중단합니다 + +edit('myfunction.m') % 편집기에서 함수/스크립트를 엽니다 +type('myfunction.m') % 함수/스크립트의 소스를 명령 창에 인쇄합니다 + +profile on % 코드 프로파일러를 켭니다 +profile off % 코드 프로파일러를 끕니다 +profile viewer % 프로파일러를 엽니다 + +help command % 명령 창에 명령어에 대한 문서를 표시합니다 +doc command % 도움말 창에 명령어에 대한 문서를 표시합니다 +lookfor command % 모든 함수의 첫 번째 주석 줄에서 명령어를 검색합니다 +lookfor command -all % 모든 함수에서 명령어를 검색합니다 + + +% 출력 서식 +format short % 부동 소수점 수에서 소수점 4자리 +format long % 소수점 15자리 +format bank % 소수점 뒤 두 자리만 - 금융 계산용 +fprintf('text') % 화면에 "text"를 인쇄합니다 +disp('text') % 화면에 "text"를 인쇄합니다 + +% 변수 및 표현식 +myVariable = 4 % 작업 공간 창에 새로 생성된 변수가 표시됩니다 +myVariable = 4; % 세미콜론은 명령 창에 출력을 억제합니다 +4 + 6 % ans = 10 +8 * myVariable % ans = 32 +2 ^ 3 % ans = 8 +a = 2; b = 3; +c = exp(a)*sin(pi/2) % c = 7.3891 + +% 함수 호출은 두 가지 방법 중 하나로 수행할 수 있습니다: +% 표준 함수 구문: +load('myFile.mat', 'y') % 괄호 안에 인수를 쉼표로 구분하여 전달합니다 +% 명령어 구문: +load myFile.mat y % 괄호 없음, 쉼표 대신 공백 사용 +% 명령어 형식에서는 따옴표가 없음에 유의하십시오: 입력은 항상 +% 리터럴 텍스트로 전달됩니다 - 변수 값을 전달할 수 없습니다. 또한 출력을 받을 수 없습니다: +[V,D] = eig(A); % 이것은 명령어 형식에 해당하는 것이 없습니다 +[~,D] = eig(A); % V가 아닌 D만 원하는 경우 + + + +% 논리 +1 > 5 % ans = 0 +10 >= 10 % ans = 1 +3 ~= 4 % 같지 않음 -> ans = 1 +3 == 3 % 같음 -> ans = 1 +3 > 1 && 4 > 1 % AND -> ans = 1 +3 > 1 || 4 > 1 % OR -> ans = 1 +~1 % NOT -> ans = 0 + +% 논리는 행렬에 적용될 수 있습니다: +A > 5 +% 각 요소에 대해 조건이 참이면 반환된 행렬에서 해당 요소는 1입니다 +A( A > 5 ) +% 조건이 참인 A의 요소를 포함하는 벡터를 반환합니다 + +% 문자열 +a = 'MyString' +length(a) % ans = 8 +a(2) % ans = y +[a,a] % ans = MyStringMyString + + +% 셀 +a = {'one', 'two', 'three'} +a(1) % ans = 'one' - 셀을 반환합니다 +char(a(1)) % ans = one - 문자열을 반환합니다 + +% 구조체 +A.b = {'one','two'}; +A.c = [1 2]; +A.d.e = false; + +% 벡터 +x = [4 32 53 7 1] +x(2) % ans = 32, MATLAB의 인덱스는 0이 아닌 1부터 시작합니다 +x(2:3) % ans = 32 53 +x(2:end) % ans = 32 53 7 1 + +x = [4; 32; 53; 7; 1] % 열 벡터 + +x = [1:10] % x = 1 2 3 4 5 6 7 8 9 10 +x = [1:2:10] % 2씩 증가, 즉 x = 1 3 5 7 9 + +% 행렬 +A = [1 2 3; 4 5 6; 7 8 9] +% 행은 세미콜론으로 구분됩니다. 요소는 공백이나 쉼표로 구분됩니다. +% A = + +% 1 2 3 +% 4 5 6 +% 7 8 9 + +A(2,3) % ans = 6, A(행, 열) +A(6) % ans = 8 +% (암시적으로 열을 벡터로 연결한 다음 해당 벡터를 인덱싱합니다) + + +A(2,3) = 42 % 2행 3열을 42로 업데이트합니다 +% A = + +% 1 2 3 +% 4 5 42 +% 7 8 9 + +A(2:3,2:3) % 이전 행렬에서 새 행렬을 만듭니다 +%ans = + +% 5 42 +% 8 9 + +A(:,1) % 1열의 모든 행 +%ans = + +% 1 +% 4 +% 7 + +A(1,:) % 1행의 모든 열 +%ans = + +% 1 2 3 + +[A ; A] % 행렬 연결 (수직) +%ans = + +% 1 2 3 +% 4 5 42 +% 7 8 9 +% 1 2 3 +% 4 5 42 +% 7 8 9 + +% 이것은 다음과 같습니다 +vertcat(A,A); + + +[A , A] % 행렬 연결 (수평) + +%ans = + +% 1 2 3 1 2 3 +% 4 5 42 4 5 42 +% 7 8 9 7 8 9 + +% 이것은 다음과 같습니다 +horzcat(A,A); + + +A(:, [3 1 2]) % 원본 행렬의 열을 재정렬합니다 +%ans = + +% 3 1 2 +% 42 4 5 +% 9 7 8 + +size(A) % ans = 3 3 + +A(1, :) =[] % 행렬의 첫 번째 행을 삭제합니다 +A(:, 1) =[] % 행렬의 첫 번째 열을 삭제합니다 + +transpose(A) % 행렬을 전치합니다. 다음과 같습니다: +A.' % 전치의 간결한 버전 (복소 공액을 취하지 않음) +ctranspose(A) % 행렬을 에르미트 전치합니다. 다음과 같습니다: +A' % 복소 전치의 간결한 버전 + % (전치 후 각 요소의 복소 공액을 취함) + + + + + +% 요소별 산술 대 행렬 산술 +% 산술 연산자는 자체적으로 전체 행렬에 작용합니다. 앞에 +% 마침표가 있으면 대신 각 요소에 작용합니다. 예: +A * B % 행렬 곱셈 +A .* B % A의 각 요소를 B의 해당 요소와 곱합니다 + +% 여러 쌍의 함수가 있으며, 하나는 각 요소에 작용하고 +% 다른 하나(이름이 m으로 끝남)는 전체 행렬에 작용합니다. +exp(A) % 각 요소를 지수화합니다 +expm(A) % 행렬 지수를 계산합니다 +sqrt(A) % 각 요소의 제곱근을 취합니다 +sqrtm(A) % 제곱이 A인 행렬을 찾습니다 + + +% 플로팅 +x = 0:.10:2*pi; % 0에서 시작하여 2*pi에서 끝나고 .1씩 증가하는 벡터를 만듭니다 +y = sin(x); +plot(x,y) +xlabel('x 축') +ylabel('y 축') +title('y = sin(x)의 플롯') +axis([0 2*pi -1 1]) % x 범위는 0에서 2*pi, y 범위는 -1에서 1 + +plot(x,y1,'-',x,y2,'--',x,y3,':') % 한 플롯에 여러 함수를 표시합니다 +legend('선 1 레이블', '선 2 레이블') % 범례로 곡선에 레이블을 지정합니다 + +% 한 플롯에 여러 함수를 표시하는 다른 방법. +% 'hold'가 켜져 있는 동안 명령어는 기존 그래프를 대체하는 대신 추가됩니다. +plot(x, y) +hold on +plot(x, z) +hold off + +loglog(x, y) % 로그-로그 플롯 +semilogx(x, y) % x축이 로그 스케일인 플롯 +semilogy(x, y) % y축이 로그 스케일인 플롯 + +fplot (@(x) x^2, [2,5]) % x=2에서 x=5까지 함수 x^2를 플로팅합니다 + +grid on % 그리드 표시; 'grid off'로 끕니다 +axis square % 현재 축 영역을 정사각형으로 만듭니다 +axis equal % 데이터 단위가 모든 방향에서 동일하도록 가로세로 비율을 설정합니다 + +scatter(x, y); % 산점도 +hist(x); % 히스토그램 +stem(x); % 값을 줄기로 플로팅하여 이산 데이터를 표시하는 데 유용합니다 +bar(x); % 막대 그래프 + +z = sin(x); +plot3(x,y,z); % 3D 선 플롯 + +pcolor(A) % 행렬의 히트맵: 값에 따라 색상이 지정된 사각형 그리드로 플로팅합니다 +contour(A) % 행렬의 등고선 플롯 +mesh(A) % 메시 표면으로 플로팅합니다 + +h = figure % 핸들 h를 사용하여 새 그림 객체를 만듭니다 +figure(h) % 핸들 h에 해당하는 그림을 현재 그림으로 만듭니다 +close(h) % 핸들 h를 사용하여 그림을 닫습니다 +close all % 모든 열린 그림 창을 닫습니다 +close % 현재 그림 창을 닫습니다 + +shg % 기존 그래픽 창을 앞으로 가져오거나 필요한 경우 새 창을 만듭니다 +clf clear % 현재 그림 창을 지우고 대부분의 그림 속성을 재설정합니다 + +% 속성은 그림 핸들을 통해 설정하고 변경할 수 있습니다. +% 그림을 만들 때 핸들을 저장할 수 있습니다. +% get 함수는 현재 그림에 대한 핸들을 반환합니다 +h = plot(x, y); % 그림을 만들 때 핸들을 저장할 수 있습니다 +set(h, 'Color', 'r') +% 'y' 노란색; 'm' 자홍색, 'c' 청록색, 'r' 빨간색, 'g' 녹색, 'b' 파란색, 'w' 흰색, 'k' 검은색 +set(h, 'LineStyle', '--') + % '--'는 실선, '---' 파선, ':' 점선, '-.' 파선-점선, 'none'은 선 없음 +get(h, 'LineStyle') + + +% gca 함수는 현재 그림의 축에 대한 핸들을 반환합니다 +set(gca, 'XDir', 'reverse'); % x축의 방향을 반전시킵니다 + +% 타일 위치에 여러 축을 포함하는 그림을 만들려면 subplot을 사용하십시오 +subplot(2,3,1); % 2x3 서브플롯 그리드의 첫 번째 위치를 선택합니다 +plot(x1); title('첫 번째 플롯') % 이 위치에 무언가를 플로팅합니다 +subplot(2,3,2); % 그리드의 두 번째 위치를 선택합니다 +plot(x2); title('두 번째 플롯') % 거기에 무언가를 플로팅합니다 + + +% 함수나 스크립트를 사용하려면 경로 또는 현재 디렉토리에 있어야 합니다 +path % 현재 경로 표시 +addpath /path/to/dir % 경로에 추가 +rmpath /path/to/dir % 경로에서 제거 +cd /path/to/move/into % 디렉토리 변경 + + +% 변수는 .mat 파일에 저장할 수 있습니다 +save('myFileName.mat') % 작업 공간의 변수를 저장합니다 +load('myFileName.mat') % 저장된 변수를 작업 공간으로 로드합니다 + +% M-파일 스크립트 +% 스크립트 파일은 일련의 문장을 포함하는 외부 파일입니다. +% 명령 창에서 동일한 코드를 반복적으로 입력하는 것을 피할 수 있습니다 +% .m 확장자를 가집니다 + +% M-파일 함수 +% 스크립트와 같고 동일한 .m 확장자를 가집니다 +% 그러나 입력 인수를 받고 출력을 반환할 수 있습니다 +% 또한 자체 작업 공간(즉, 다른 변수 범위)을 가집니다. +% 함수 이름은 파일 이름과 일치해야 합니다(따라서 이 예제를 double_input.m으로 저장하십시오). +% 'help double_input.m'은 함수 시작 줄 아래의 주석을 반환합니다 +function output = double_input(x) + %double_input(x)은 x 값의 두 배를 반환합니다 + output = 2*x; +end +double_input(6) % ans = 12 + + +% 하위 함수 및 중첩 함수도 가질 수 있습니다. +% 하위 함수는 기본 함수와 동일한 파일에 있으며 파일의 함수에서만 +% 호출할 수 있습니다. 중첩 함수는 다른 함수 내에 정의되며 +% 해당 작업 공간과 자체 작업 공간 모두에 액세스할 수 있습니다. + +% 새 파일을 만들지 않고 함수를 만들고 싶다면 +% 익명 함수를 사용할 수 있습니다. 다른 함수에 전달할 함수를 +% 빠르게 정의할 때 유용합니다(예: fplot으로 플로팅, quad로 부정적분 평가, +% fzero로 근 찾기 또는 fminsearch로 최소값 찾기). +% 입력의 제곱을 반환하는 예제, 핸들 sqr에 할당됨: +sqr = @(x) x.^2; +sqr(10) % ans = 100 +doc function_handle % 자세히 알아보기 + +% 사용자 입력 +a = input('값을 입력하십시오: ') + +% 파일 실행을 중지하고 키보드에 제어권을 부여합니다: 사용자는 +% 변수를 검사하거나 변경할 수 있습니다. 실행을 계속하려면 'return'을 입력하고 +% 종료하려면 'dbquit'를 입력하십시오 +keyboard + +% 데이터 읽기 (excel/CSV/이미지 파일의 경우 xlsread/importdata/imread도 사용) +fopen(filename) + +% 출력 +disp(a) % 변수 a의 값을 인쇄합니다 +disp('Hello World') % 문자열을 인쇄합니다 +fprintf % 더 많은 제어로 명령 창에 인쇄합니다 + +% 조건문 (괄호는 선택 사항이지만 좋은 스타일입니다) +if (a > 23) + disp('23보다 큼') +elseif (a == 23) + disp('a는 23입니다') +else + disp('두 조건 모두 충족되지 않음') +end + +% 반복 +% NB. 벡터/행렬의 요소를 반복하는 것은 느립니다! +% 가능한 경우 한 번에 전체 벡터/행렬에 작용하는 함수를 사용하십시오 +for k = 1:5 + disp(k) +end + +k = 0; +while (k < 5) + k = k + 1; +end + +% 코드 실행 시간 측정: 'toc'는 'tic'이 호출된 이후의 시간을 인쇄합니다 +tic +A = rand(1000); +A*A*A*A*A*A*A; +toc + +% MySQL 데이터베이스에 연결 +dbname = 'database_name'; +username = 'root'; +password = 'root'; +driver = 'com.mysql.jdbc.Driver'; +dburl = ['jdbc:mysql://localhost:8889/' dbname]; +javaclasspath('mysql-connector-java-5.1.xx-bin.jar'); %xx는 버전에 따라 다름, http://dev.mysql.com/downloads/connector/j/에서 다운로드 가능 +conn = database(dbname, username, password, driver, dburl); +sql = ['SELECT * from table_name where id = 22'] % 예제 sql 문 +a = fetch(conn, sql) %a에 데이터가 포함됩니다 + + +% 일반적인 수학 함수 +sin(x) +cos(x) +tan(x) +asin(x) +acos(x) +atan(x) +exp(x) +sqrt(x) +log(x) +log10(x) +abs(x) %x가 복소수이면 크기를 반환합니다 +min(x) +max(x) +ceil(x) +floor(x) +round(x) +rem(x) +rand % 균일하게 분포된 의사 난수 +randi % 균일하게 분포된 의사 난수 정수 +randn % 정규 분포된 의사 난수 + +%복소수 수학 연산 +abs(x) % 복소 변수 x의 크기 +phase(x) % 복소 변수 x의 위상(또는 각도) +real(x) % x의 실수부를 반환합니다 (즉, x = a +jb이면 a를 반환) +imag(x) % x의 허수부를 반환합니다 (즉, x = a+jb이면 b를 반환) +conj(x) % 복소 공액을 반환합니다 + + +% 일반적인 상수 +pi +NaN +inf + +% 행렬 방정식 풀기 (해가 없으면 최소 제곱 해를 반환) +% \ 및 / 연산자는 mldivide 및 mrdivide 함수와 동일합니다 +x=A\b % Ax=b를 풉니다. inv(A)*b를 사용하는 것보다 빠르고 수치적으로 더 정확합니다. +x=b/A % xA=b를 풉니다 + +inv(A) % 역행렬 계산 +pinv(A) % 유사 역행렬 계산 + +% 일반적인 행렬 함수 +zeros(m,n) % 0으로 채워진 m x n 행렬 +ones(m,n) % 1로 채워진 m x n 행렬 +diag(A) % 행렬 A의 대각선 요소를 추출합니다 +diag(x) % 대각선 요소가 x에 나열되고 다른 곳은 0인 행렬을 구성합니다 +eye(m,n) % 단위 행렬 +linspace(x1, x2, n) % 최소 x1, 최대 x2로 n개의 등간격 점을 반환합니다 +inv(A) % 행렬 A의 역행렬 +det(A) % A의 행렬식 +eig(A) % A의 고유값 및 고유 벡터 +trace(A) % 행렬의 트레이스 - sum(diag(A))와 동일 +isempty(A) % 배열이 비어 있는지 테스트합니다 +all(A) % 모든 요소가 0이 아니거나 참인지 테스트합니다 +any(A) % 요소 중 0이 아니거나 참인 것이 있는지 테스트합니다 +isequal(A, B) % 두 배열의 동등성을 테스트합니다 +numel(A) % 행렬의 요소 수 +triu(x) % x의 상삼각 부분을 반환합니다 +tril(x) % x의 하삼각 부분을 반환합니다 +cross(A,B) % 벡터 A와 B의 외적을 반환합니다 +dot(A,B) % 두 벡터의 스칼라 곱을 반환합니다 (길이가 같아야 함) +transpose(A) % A의 전치를 반환합니다 +fliplr(A) % 행렬을 왼쪽에서 오른쪽으로 뒤집습니다 +flipud(A) % 행렬을 위에서 아래로 뒤집습니다 + +% 행렬 분해 +[L, U, P] = lu(A) % LU 분해: PA = LU, L은 하삼각, U는 상삼각, P는 순열 행렬 +[P, D] = eig(A) % 고유값 분해: AP = PD, P의 열은 고유 벡터, D의 대각선은 고유값 +[U,S,V] = svd(X) % SVD: XV = US, U와 V는 유니터리 행렬, S는 감소 순서로 음이 아닌 대각선 요소를 가짐 + +% 일반적인 벡터 함수 +max % 가장 큰 구성 요소 +min % 가장 작은 구성 요소 +length % 벡터의 길이 +sort % 오름차순 정렬 +sum % 요소의 합 +prod % 요소의 곱 +mode % 최빈값 +median % 중앙값 +mean % 평균값 +std % 표준 편차 +perms(x) % x 요소의 모든 순열 나열 +find(x) % x의 모든 0이 아닌 요소를 찾아 인덱스를 반환하며, 비교 연산자를 사용할 수 있습니다. + % 즉, find( x == 3 )은 3과 같은 요소의 인덱스를 반환합니다 + % 즉, find( x >= 3 )은 3보다 크거나 같은 요소의 인덱스를 반환합니다 + + +% 클래스 +% MATLAB은 객체 지향 프로그래밍을 지원할 수 있습니다. +% 클래스는 .m 확장자를 가진 클래스 이름의 파일에 넣어야 합니다. +% 시작하려면 GPS 웨이포인트를 저장하는 간단한 클래스를 만듭니다. +% WaypointClass.m 시작 +classdef WaypointClass % 클래스 이름. + properties % 클래스의 속성은 구조체처럼 작동합니다 + latitude + longitude + end + methods + % 클래스와 이름이 같은 이 메서드는 생성자입니다. + function obj = WaypointClass(lat, lon) + obj.latitude = lat; + obj.longitude = lon; + end + + % Waypoint 객체를 사용하는 다른 함수 + function r = multiplyLatBy(obj, n) + r = n*[obj.latitude]; + end + + % 특수 함수를 호출하지 않고 두 Waypoint 객체를 더하고 싶다면 + % 다음과 같이 MATLAB의 산술을 오버로드할 수 있습니다: + function r = plus(o1,o2) + r = WaypointClass([o1.latitude] +[o2.latitude], ... + [o1.longitude]+[o2.longitude]); + end + end +end +% WaypointClass.m 끝 + +% 생성자를 사용하여 클래스의 객체를 만들 수 있습니다 +a = WaypointClass(45.0, 45.0) + +% 클래스 속성은 MATLAB 구조체와 똑같이 작동합니다. +a.latitude = 70.0 +a.longitude = 25.0 + +% 메서드는 함수와 같은 방식으로 호출할 수 있습니다 +ans = multiplyLatBy(a,3) + +% 메서드는 점 표기법을 사용하여 호출할 수도 있습니다. 이 경우 객체를 +% 메서드에 전달할 필요가 없습니다. +ans = a.multiplyLatBy(1/3) + +% MATLAB 함수는 객체를 처리하도록 오버로드될 수 있습니다. +% 위 메서드에서 MATLAB이 두 Waypoint 객체의 +% 덧셈을 처리하는 방법을 오버로드했습니다. +b = WaypointClass(15.0, 32.0) +c = a + b +``` + +## MATLAB에 대해 더 알아보기 + +* [공식 웹사이트](http://www.mathworks.com/products/matlab/) +* [공식 MATLAB 답변 포럼](http://www.mathworks.com/matlabcentral/answers/) +* [Loren on the Art of MATLAB](http://blogs.mathworks.com/loren/) +* [Cleve's Corner](http://blogs.mathworks.com/cleve/) diff --git a/ko/mercurial.md b/ko/mercurial.md new file mode 100644 index 0000000000..33663b2bac --- /dev/null +++ b/ko/mercurial.md @@ -0,0 +1,315 @@ +--- +category: tool +name: Mercurial +contributors: + - ["Will L. Fife", "http://github.com/sarlalian"] +filename: LearnMercurial.txt +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Mercurial은 무료 분산 소스 제어 관리 도구입니다. +직관적인 인터페이스를 사용하면서 모든 크기의 프로젝트를 효율적으로 처리할 수 있는 강력한 기능을 제공합니다. 사용하기 쉽고 깨지기 어려워 버전 관리된 파일로 작업하는 모든 사람에게 이상적입니다. + +## 버전 관리 개념 + +### 버전 관리란 무엇인가? + +버전 관리는 시간 경과에 따른 파일 및/또는 디렉토리 집합의 변경 사항을 추적하는 시스템입니다. + +### 왜 Mercurial을 사용하는가? + +* 분산 아키텍처 - 전통적으로 CVS 및 Subversion과 같은 버전 제어 시스템은 프로젝트의 개정 기록을 저장하는 중앙 서버가 있는 클라이언트 서버 아키텍처입니다. 그러나 Mercurial은 진정한 분산 아키텍처로, 각 개발자에게 전체 개발 기록의 전체 로컬 복사본을 제공합니다. 중앙 서버와 독립적으로 작동합니다. +* 빠름 - 전통적으로 CVS 및 Subversion과 같은 버전 제어 시스템은 프로젝트의 개정 기록을 저장하는 중앙 서버가 있는 클라이언트 서버 아키텍처입니다. 그러나 Mercurial은 진정한 분산 아키텍처로, 각 개발자에게 전체 개발 기록의 전체 로컬 복사본을 제공합니다. 중앙 서버와 독립적으로 작동합니다. +* 플랫폼 독립적 - Mercurial은 플랫폼 독립적으로 작성되었습니다. Mercurial의 대부분은 Python으로 작성되었으며, 작은 성능이 중요한 부분은 이식 가능한 C로 작성되었습니다. 바이너리 릴리스는 모든 주요 플랫폼에서 사용할 수 있습니다. +* 확장 가능 - Mercurial의 기능은 Mercurial과 함께 제공되는 공식 확장을 활성화하거나 [위키에서 다운로드](https://www.mercurial-scm.org/wiki/UsingExtensions)하거나 [직접 작성](https://www.mercurial-scm.org/wiki/WritingExtensions)하여 확장할 수 있습니다. 확장은 Python으로 작성되었으며 기본 명령의 작동 방식을 변경하고, 새 명령을 추가하고, Mercurial의 모든 핵심 기능에 액세스할 수 있습니다. +* 사용하기 쉬움 - Mercurial 명령 세트는 Subversion 사용자가 기대하는 것과 일치하므로 집처럼 편안하게 느낄 것입니다. 대부분의 위험한 작업은 사용하려면 활성화해야 하는 확장의 일부입니다. +* 오픈 소스 - Mercurial은 [GNU 일반 공중 사용 허가서 버전 2](http://www.gnu.org/licenses/gpl-2.0.txt) 또는 이후 버전의 조건에 따라 라이선스가 부여된 무료 소프트웨어입니다. + +## 용어 + +| 용어 | 정의 | +| ------------- | ---------------------------------- | +| 저장소 | 저장소는 개정판 모음입니다. | +| hgrc | 저장소의 기본값을 저장하는 구성 파일입니다. | +| 개정판 | 커밋된 변경 집합: REV 번호가 있습니다. | +| 변경 집합 | 차이점으로 저장된 변경 사항 집합 | +| 차이점 | 파일 간의 변경 사항 | +| 태그 | 명명된 개정판 | +| 부모 | 개정판의 직계 조상 | +| 브랜치 | 개정판의 자식 | +| 헤드 | 헤드는 자식 변경 집합이 없는 변경 집합입니다. | +| 병합 | 두 개의 HEAD를 병합하는 프로세스 | +| 팁 | 모든 브랜치의 최신 개정판 | +| 패치 | 두 개정판 간의 모든 차이점 | +| 번들 | 권한 및 이름 바꾸기 지원이 포함된 패치 | + +## 명령어 + +### init + +지정된 디렉토리에 새 저장소를 생성하며, 설정 및 저장된 정보는 `.hg`라는 디렉토리에 있습니다. + +```bash +$ hg init +``` + +### help + +각 명령어에 대한 매우 자세한 설명에 액세스할 수 있습니다. + +```bash +# 사용 가능한 명령어를 빠르게 확인 +$ hg help + +# 특정 명령어에 대한 도움말 얻기 +# hg help +$ hg help add +$ hg help commit +$ hg help init +``` + +### status + +디스크에 있는 것과 현재 브랜치 또는 태그에 커밋된 것 사이의 차이점을 표시합니다. + +```bash +# 파일 상태 표시 +$ hg status + +# 상태 하위 명령어에 대한 도움말 얻기 +$ hg help status +``` + +### add + +다음 커밋 시 지정된 파일을 저장소에 추가합니다. + +```bash +# 현재 디렉토리에 파일 추가 +$ hg add filename.rb + +# 하위 디렉토리에 파일 추가 +$ hg add foo/bar/filename.rb + +# 패턴별로 파일 추가 +$ hg add *.rb +``` + +### branch + +현재 브랜치 이름을 설정하거나 표시합니다. + +*브랜치 이름은 영구적이고 전역적입니다. 대신 가벼운 북마크를 만들려면 'hg bookmark'를 사용하십시오. 명명된 브랜치와 북마크에 대한 자세한 내용은 'hg help glossary'를 참조하십시오.* + +```bash +# 인수가 없으면 현재 브랜치 이름을 표시합니다. +$ hg branch + +# 이름 인수가 있으면 현재 브랜치를 변경합니다. +$ hg branch new_branch +작업 디렉토리를 브랜치 new_branch로 표시했습니다. +(브랜치는 영구적이고 전역적입니다. 북마크를 원하셨습니까?) +``` + +### tag + +현재 또는 지정된 개정판에 하나 이상의 태그를 추가합니다. + +태그는 저장소의 특정 개정판에 이름을 지정하는 데 사용되며, 다른 개정판을 비교하거나, 중요한 이전 버전으로 돌아가거나, 브랜치 지점을 릴리스로 표시하는 데 매우 유용합니다. 기존 태그를 변경하는 것은 일반적으로 허용되지 않습니다. 재정의하려면 -f/--force를 사용하십시오. + +```bash +# 태그 목록 +$ hg tags +tip 2:efc8222cd1fb +v1.0 0:37e9b57123b3 + +# 현재 개정판에 새 태그 생성 +$ hg tag v1.1 + +# 특정 개정판에 태그 생성 +$ hg tag -r efc8222cd1fb v1.1.1 +``` + +### clone + +기존 저장소의 복사본을 새 디렉토리에 생성합니다. + +대상 디렉토리 이름이 지정되지 않은 경우 소스의 기본 이름으로 기본 설정됩니다. + +```bash +# 원격 저장소를 로컬 디렉토리로 복제 +$ hg clone https://some-mercurial-server.example.com/reponame + +# 로컬 저장소를 원격 서버로 복제 +$ hg clone . ssh://username@some-mercurial-server.example.com/newrepo + +# 로컬 저장소를 로컬 저장소로 복제 +$ hg clone . /tmp/some_backup_dir +``` + +### commit / ci + +지정된 파일의 변경 사항을 저장소에 커밋합니다. + +```bash +# 메시지와 함께 커밋 +$ hg commit -m 'This is a commit message' + +# 현재 트리의 모든 추가/제거된 파일 커밋 +$ hg commit -A 'Adding and removing all existing files in the tree' + +# 'hg status'가 현재 보고하는 변경 사항 외에 부모의 변경 사항을 +# 포함하는 새 커밋으로 작업 디렉토리의 부모를 수정합니다. +$ hg commit --amend -m "Correct message" +``` + +### diff + +통합 diff 형식을 사용하여 지정된 파일에 대한 개정판 간의 차이점을 표시합니다. + +```bash +# 현재 디렉토리와 이전 개정판 간의 차이점 표시 +$ hg diff -r 10 + +# 두 이전 개정판 간의 차이점 표시 +$ hg diff -r 30 -r 20 +``` + +### grep + +지정된 파일에서 패턴에 대한 개정 기록을 검색합니다. + +```bash +# 특정 구문에 대한 파일 검색 +$ hg grep "TODO:" +``` + +### log / history + +전체 저장소 또는 파일의 개정 기록을 표시합니다. 개정 범위가 지정되지 않은 경우 --follow가 설정되지 않은 한 기본값은 "tip:0"이며, 이 경우 작업 디렉토리 부모가 시작 개정판으로 사용됩니다. + +```bash +# 전체 저장소의 기록 표시 +$ hg log + +# 단일 파일의 기록 표시 +$ hg log myfile.rb + +# 가장 최근 변경 집합이 맨 위에 있는 ASCII 아트 DAG로 +# 개정 변경 사항을 표시합니다. +$ hg log -G +``` + +### merge + +다른 개정판을 작업 디렉토리로 병합합니다. + +```bash +# 로컬 저장소에 변경 집합 병합 +$ hg merge + +# 명명된 브랜치 또는 개정판에서 현재 로컬 브랜치로 병합 +$ hg merge branchname_or_revision + +# 성공적인 병합 후 변경 사항 커밋 +hg commit +``` + +### move / mv / rename + +파일 이름 바꾸기; 복사 + 제거와 동일합니다. 대상을 소스의 복사본으로 표시하고, 소스를 삭제하도록 표시합니다. 대상이 디렉토리인 경우 복사본이 해당 디렉토리에 배치됩니다. 대상이 파일인 경우 소스는 하나만 있을 수 있습니다. + +```bash +# 단일 파일 이름 바꾸기 +$ hg mv foo.txt bar.txt + +# 디렉토리 이름 바꾸기 +$ hg mv some_directory new_directory +``` + +### pull + +원격 저장소에서 로컬 저장소로 변경 사항을 가져옵니다. + +```bash +# 원격 경로 목록 +$ hg paths +remote1 = http://path/to/remote1 +remote2 = http://path/to/remote2 + +# 원격 1에서 가져오기 +$ hg pull remote1 + +# 원격 2에서 가져오기 +$ hg pull remote2 +``` + +### push + +로컬 저장소의 변경 집합을 지정된 대상으로 푸시합니다. + +```bash +# 원격 경로 목록 +$ hg paths +remote1 = http://path/to/remote1 +remote2 = http://path/to/remote2 + +# 원격 1에서 푸시 +$ hg push remote1 + +# 원격 2에서 푸시 +$ hg push remote2 +``` + +### rebase + +변경 집합(및 하위 항목)을 다른 브랜치로 이동합니다. + +리베이스는 반복적인 병합을 사용하여 기록의 한 부분(소스)에서 다른 부분(대상)으로 변경 집합을 이식합니다. 이는 마스터 개발 트리에 대한 *로컬* 변경 사항을 선형화하는 데 유용할 수 있습니다. + +* 커밋을 소스 개정판으로 다시 초안으로 작성합니다. +* -s는 소스, 즉 리베이스하는 대상입니다. +* -d는 대상, 즉 보내는 곳입니다. + +```bash +# 커밋을 초안 상태로 만듭니다. +# 이것은 관련 브랜치의 모든 후속 커밋을 초안으로 만듭니다. +$ hg phase --draft --force -r 1206 + +# 개정 102에서 개정 208로 리베이스 +$ hg rebase -s 102 -d 208 +``` + +### revert + +파일을 체크아웃 상태로 복원합니다. 개정판이 지정되지 않은 경우 지정된 파일 또는 디렉토리를 작업 디렉토리의 부모에 있던 내용으로 되돌립니다. 이렇게 하면 파일의 내용을 수정되지 않은 상태로 복원하고 추가, 제거, 복사 및 이름 바꾸기를 예약 취소합니다. 작업 디렉토리에 두 개의 부모가 있는 경우 개정판을 명시적으로 지정해야 합니다. + +```bash +# 특정 파일을 체크아웃 상태로 재설정 +$ hg revert oops_i_did_it_again.txt + +# .orig 파일을 남기지 않고 특정 파일을 체크아웃 상태로 되돌리기 +$ hg revert -C oops_i_did_it_again.txt + +# 모든 변경 사항 되돌리기 +$ hg revert -a +``` + +### rm / remove + +다음 커밋 시 지정된 파일을 제거합니다. + +```bash +# 특정 파일 제거 +$ hg remove go_away.txt + +# 패턴별로 파일 그룹 제거 +$ hg remove *.txt +``` + +## 추가 정보 + +* [워크플로에서 Mercurial 배우기](https://www.mercurial-scm.org/guide) +* [Mercurial 빠른 시작](https://www.mercurial-scm.org/wiki/QuickStart) +* [Mercurial: Bryan O'Sullivan의 결정판 가이드](http://hgbook.red-bean.com/) diff --git a/ko/mercury.md b/ko/mercury.md new file mode 100644 index 0000000000..deb41f431b --- /dev/null +++ b/ko/mercury.md @@ -0,0 +1,263 @@ +--- +name: Mercury +contributors: + - ["Julian Fondren", "https://mercury-in.space/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Mercury는 Prolog, ML, Haskell의 영향을 받은 엄격하고 순수한 함수형/논리형 프로그래밍 언어입니다. + +```prolog +% 퍼센트 기호는 한 줄 주석을 시작합니다. + + % foo(Bar, Baz) + % + % 문서 주석은 설명하는 내용 앞에 들여쓰기됩니다. +:- pred foo(bar::in, baz::out) is det. + +% 모든 최상위 구문 요소는 '.'(마침표)로 끝납니다. + +% Mercury 용어는 술어 논리에서 유래했습니다. 대략적으로: + +% | Mercury | C | +% | | | +% | Goal (목표) | statement (문장) | +% | expression (표현식) | expression (표현식) | +% | predicate rule (술어 규칙) | void function (void 함수) | +% | function rule (함수 규칙) | function (함수) | +% | head (of a rule) (헤드) | function name and parameters | +% | body (of a rule) (본문) | function body (함수 본문) | +% | fact (사실) | (rule without a body) (본문 없는 규칙) | +% | pred/func declaration | function signature (함수 시그니처) | +% | A, B (논리곱) | A && B | +% | A ; B (논리합) | if (A) {} else if (B) {} | + +% 몇 가지 사실: +man(socrates). % "소크라테스는 사람이라는 사실입니다" +man(plato). +man(aristotle). + +% 규칙: +mortal(X) :- man(X). % "X가 사람이면 X는 필멸자라는 규칙입니다." +% ^^^^^^-- 규칙의 본문 +% ^^-- 화살표 <--, 본문에서 헤드를 가리킴 +%^^^^^^^^-- 규칙의 헤드 +% 이것은 또한 규칙을 정의하는 단일 절입니다. + +% X가 대문자라는 것으로 변수임을 알 수 있습니다. +% socrates가 소문자라는 것으로 항임을 알 수 있습니다. + +% 'socrates'가 정의되지 않으면 오류입니다. 타입을 가져야 합니다: + +% 선언은 ':-'로 시작합니다. +:- type people + ---> socrates + ; plato + ; aristotle + ; hermes. + %<--첫 번째 탭 정지 (4칸 탭 사용) + %<--세 번째 탭 정지 (---> 다음 첫 번째) + +:- pred man(people). % 규칙과 사실도 타입이 필요합니다. + +% 규칙의 모드는 사용 방법을 알려줍니다. +:- mode man(in) is semidet. % man(plato)는 성공합니다. man(hermes)는 실패합니다. +:- mode man(out) is multi. % man(X)는 X를 socrates, plato, aristotle 중 하나에 바인딩합니다. + +% semidet 술어는 테스트와 같습니다. 값을 반환하지는 않지만 +% 성공하거나 실패할 수 있으며, 백트래킹이나 논리합 또는 +% 조건문의 다른 쪽을 트리거합니다. + +% 'is semidet'은 모드의 결정성을 제공합니다. 다른 결정성: +% | 실패할 수 있는가? | 해 없음 | 1개 | 1개 이상 | +% | | | | | +% | 아니요 | 오류 | det | multi | +% | 예 | 실패 | semidet | nondet | + +:- pred mortal(people::in) is semidet. % 하나의 선언에 타입/모드 + +% 이 규칙의 본문은 두 개의 논리곱 A, B, C로 구성됩니다. +% 이 규칙은 A, B, C가 모두 참일 때 참입니다. +% age(P)가 16을 반환하면 실패합니다. +% alive(P)가 실패하면 실패합니다. +:- type voter(people::in) is semidet. +voter(P) :- + alive(P), + registered(P, locale(P)), + age(P) >= 18. % age/1은 함수이고, int.>=는 연산자로 사용되는 함수입니다. + +% "P는 살아있고, P의 지역에 등록되어 있으며, P의 나이가 +% 18세 이상이면 유권자입니다." + +% 여기에 사용된 >=는 기본적으로 가져오지 않는 'int' 모듈에서 제공됩니다. +% Mercury는 매우 작은 'Prelude'('builtin' 모듈)를 가지고 있습니다. +% 리스트 리터럴을 사용하려면 'list' 모듈도 가져와야 합니다. +``` + +완전한 실행 가능 예제. 'types.m' 파일에 있으며 'mmc --make types'로 컴파일합니다. + +```prolog +:- module types. +:- interface. +:- import_module io. % io.io 타입에 필요... +% main/2는 보통 'det'입니다. 스레딩 및 예외는 'cc_multi'가 필요합니다. +:- pred main(io::di, io::uo) is cc_multi. % 프로그램 진입점 +:- implementation. +:- import_module int, float, string, list, bool, map, exception. + +% 열거형. +:- type days + ---> sunday + ; monday + ; tuesday + ; wednesday + ; thursday + ; friday + ; saturday. + +% 식별 합집합, ML의 데이터 타입과 유사. +:- type payment_method + ---> cash(int) + ; credit_card( + name :: string, % 이름 있는 필드 + cc_number :: string, + cvv :: int, + expiration :: string + ) + ; crypto(coin_type, wallet, amount). + +:- type coin_type + ---> etherium + ; monero. % "다른 코인도 사용 가능" + +% 타입 별칭. +:- type wallet == string. +:- type amount == int. + +% !IO는 io.io 인수의 쌍입니다. +% I/O를 수행하기 위해 I/O를 수행하는 모든 것에 전달하십시오. +% 그렇지 않으면 순수하지 않은 많은 함수가 !IO를 사용하여 'I/O 상태에 연결'할 수 있습니다. +main(!IO) :- + Ints = [ + 3, + 1 + 1, + 8 - 1, + 10 * 2, + 35 / 5, + 5 / 2, % 절단 나눗셈 + int.div(5, 2), % 내림 나눗셈 + div(5, 2), % (타입으로 인해 모호하지 않음) + 5 `div` 2, % (모든 이진 함수는 ``를 사용하여 연산자가 될 수 있음) + 7 `mod` 3, % 내림 나눗셈의 모듈로 + 7 `rem` 3, % 절단 나눗셈의 나머지 + 2 `pow` 4, % 2의 4제곱 + (1 + 3) * 2, % 괄호는 일반적인 의미를 가짐 + + 2 >> 3, % 비트 단위 오른쪽 시프트 + 128 << 3, % 비트 단위 왼쪽 시프트 + \ 0, % 비트 단위 보수 + 5 /\ 1, % 비트 단위 AND + 5 \/ 1, % 비트 단위 OR + 5 `xor` 3, % 비트 단위 XOR + + max_int, + min_int, + + 5 `min` 3, % ( 5 > 3 이면 3 아니면 5 ) + 5 `max` 3 + ], + Bools = [ + yes, + no + % Mercury에서는 제어 흐름이 불리언 표현식 대신 + % semidet 목표에 의해 이루어지므로 불리언은 훨씬 덜 중요합니다. + ], + Strings = [ + "이것은 문자열입니다", + "문자열은 이중화를 통해 \"\" 포함된 큰따옴표를 가질 수 있습니다", + "문자열은 일반적인 이스케이프 \u4F60\u597D\n을 지원합니다", + % 문자열의 암시적 연결 없음: "concat:" "together" + "하지만 string.++ 연산자 " ++ "를 사용할 수 있습니다", + + % 두 번째 매개변수는 list(string.poly_type)입니다. + % s/1은 문자열을 받아 poly_type을 반환하는 함수입니다. + % i/1은 int를, f/1은 float를, c/1은 char를 받습니다. + string.format("안녕하세요, %d번째 %s\n", [i(45), s("세상")]) + ], + + % 'map'과 'list'와 같은 순수 함수형 타입으로 시작하십시오! + % 배열과 해시 테이블도 사용할 수 있지만, 사용하려면 + % Mercury에 대해 훨씬 더 많이 알아야 합니다. + get_map1(Map1), + get_map2(Map2), + + % list.foldl에는 *많은* 변형이 있습니다. + % 이것은 리스트의 각 X에 대해 io.print_line(X, !IO)를 호출합니다. + foldl(io.print_line, Ints, !IO), + foldl(io.print_line, Bools, !IO), + foldl(io.print_line, Strings, !IO), + io.print_line(Map1, !IO), + % ( Cond 이면 ThenGoal 아니면 ElseGoal ) + % Cond에서 I/O 허용 안 됨: I/O는 실패할 수 없습니다! + ( if Map2^elem(42) = Elem then + io.print_line(Elem, !IO) + else % 항상 필요 + true % 아무것도 하지 않고 성공 ('fail'과 반대) + ), + + % 예외 처리: + ( try [io(!IO)] ( % io/1 매개변수 필요, 그렇지 않으면 여기서 I/O 허용 안 됨 + io.print_line(received(cash(1234)), !IO), + io.print_line(received(crypto(monero, "invalid", 123)), !IO) + ) then + io.write_string("모든 결제 수락됨\n", !IO) % 절대 도달하지 않음 + catch "monero not yet supported" -> % 매우 구체적인 catch! + io.write_string("monero 결제 실패\n", !IO) + ). + +:- pred get_map1(map(string, int)::out) is det. +get_map1(!:Map) :- % 헤드의 !:Map은 최종 (자유, 바인딩되지 않은) Map입니다. + !:Map = init, % 본문의 !:Map은 다음 Map입니다. + det_insert("hello", 1, !Map), % Map 변수 쌍 + det_insert("world", 2, !Map), + + % 현재 (바인딩된) Map의 디버그 인쇄 + % 다른 [Params]는 런타임 또는 컴파일 타임 플래그에 따라 선택 사항으로 만들 수 있습니다. + trace [io(!IO)] (io.print_line(!.Map, !IO)), + + det_insert_from_corresponding_lists(K, V, !Map), + % 이 코드는 K와 V가 사용되기 전에 정의되도록 재정렬되었습니다. + K = ["more", "words", "here"], + V = [3, 4, 5]. + +:- pred get_map2(map(int, bool)::out) is det. +get_map2(Map) :- + det_insert(42, yes, map.init, Map). + +:- func received(payment_method) = string. +received(cash(N)) = string.format("%d달러 받음", [i(N)]). +received(credit_card(_, _, _, _)) = "신용카드 받음". % _는 버리는 값 +received(crypto(Type, _Wallet, Amount)) = S :- % _Wallet은 이름 있는 버리는 값 + ( % case/switch 구조 + Type = etherium, + S = string.format("%d ETH 받는 중", [i(Amount)]) + ; + Type = monero, + throw("monero는 아직 지원되지 않음") % 문자열을 페이로드로 사용하는 예외 + ). +``` + +## 빠르네요! 더 원하시나요? + +### 추가 튜토리얼 + +* [Mercury 튜토리얼](https://mercurylang.org/documentation/papers/book.pdf) (pdf 링크) - 더 여유로운 속도의 더 전통적인 튜토리얼 +* [Mercury Crash Course](https://mercury-in.space/crash.html) - Q&A 형식의 밀도 높은 예제 중심 튜토리얼 +* [GitHub Wiki 튜토리얼](https://github.com/Mercury-Language/mercury/wiki/Tutorial) +* [Getting Started with Mercury](https://bluishcoder.co.nz/2019/06/23/getting-started-with-mercury.html) - 설치 및 첫 단계 + +### 문서 + +* 언어 매뉴얼, 사용자 가이드, 라이브러리 참조는 모두 다음에 있습니다. + [mercurylang.org](https://mercurylang.org/documentation/documentation.html) diff --git a/ko/messagepack.md b/ko/messagepack.md new file mode 100644 index 0000000000..00e828dd78 --- /dev/null +++ b/ko/messagepack.md @@ -0,0 +1,172 @@ +--- +category: framework +name: MessagePack +filename: learnmessagepack.mpac +contributors: + - ["Gabriel Chuan", "https://github.com/gczh"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +MessagePack은 효율적인 바이너리 직렬화 형식입니다. JSON처럼 여러 언어 간에 데이터를 교환할 수 있습니다. 다른 형식에 비해 더 빠르고 작다는 장점이 있습니다. + +MessagePack에서는 작은 정수가 단일 바이트로 인코딩되고, 일반적인 짧은 문자열은 문자열 자체 외에 추가로 1바이트만 필요합니다. 이로 인해 MessagePack은 유선으로 효율적인 전송에 유용합니다. + +``` +# 0. 구조 이해하기 ==== + +JSON, 40 Bytes UTF-8 + +---------------------------------------------- +| {"name":"John Doe","age":12} | +---------------------------------------------- +| {" | 7B 22 | +| name | 6E 61 6D 65 | +| ":" | 22 3A 22 | +| John Doe | 4A 6F 68 6E 20 44 6F 65 | +| "," | 22 2C 22 | +| age | 61 67 65 | +| ": | 22 3A 20 | +| 12 | 31 32 | +| } | 7D | +---------------------------------------------- + + +MessagePack, 27 Bytes UTF-8 + +---------------------------------------------- +| ‚¤name¨John Doe£age.12 | +---------------------------------------------- +| ‚¤ | 82 84 | +| name | 6E 61 6D 65 | +| ¨ | A8 | +| John Doe | 4A 6F 68 6E 20 44 6F 65 | +| £ | A3 | +| age | 61 67 65 | +| . | 0C | +| 12 | 31 32 | +---------------------------------------------- + +# 1. JAVA ==== + +""" Maven으로 설치하기 +""" + + + ... + + org.msgpack + msgpack + ${msgpack.version} + + ... + + + +""" 간단한 직렬화/역직렬화 +""" + +// 직렬화 객체 생성. +List src = new ArrayList(); +src.add("msgpack"); +src.add("kumofs"); + +MessagePack msgpack = new MessagePack(); +// 직렬화 +byte[] raw = msgpack.write(src); + +// 템플릿을 사용하여 직접 역직렬화 +List dst1 = msgpack.read(raw, Templates.tList(Templates.TString)); +System.out.println(dst1.get(0)); +System.out.println(dst1.get(1)); + +// 또는, 값으로 역직렬화한 다음 타입 변환. +Value dynamic = msgpack.read(raw); +List dst2 = new Converter(dynamic) + .read(Templates.tList(Templates.TString)); +System.out.println(dst2.get(0)); +System.out.println(dst2.get(1)); + + +# 2. RUBY ==== + +""" Gem 설치하기 +""" + +gem install msgpack + +""" 스트리밍 API +""" + +# 2개 요소 배열 [e1, e2] 직렬화 +pk = MessagePack::Packer.new(io) +pk.write_array_header(2).write(e1).write(e2).flush + +# IO에서 객체 역직렬화 +u = MessagePack::Unpacker.new(io) +u.each { |obj| ... } + +# 이벤트 기반 역직렬화 +def on_read(data) + @u ||= MessagePack::Unpacker.new + @u.feed_each(data) { |obj| ... } +end + +# 3. NODE.JS ==== + +""" NPM으로 설치하기 +""" + +npm install msgpack5 --save + +""" Node에서 사용하기 +""" + +var msgpack = require('msgpack5')() // 우리 확장 기능의 네임스페이스 + , a = new MyType(2, 'a') + , encode = msgpack.encode + , decode = msgpack.decode + +msgpack.register(0x42, MyType, mytipeEncode, mytipeDecode) + +console.log(encode({ 'hello': 'world' }).toString('hex')) +// 81a568656c6c6fa5776f726c64 +console.log(decode(encode({ 'hello': 'world' }))) +// { hello: 'world' } +console.log(encode(a).toString('hex')) +// d5426161 +console.log(decode(encode(a)) instanceof MyType) +// true +console.log(decode(encode(a))) +// { value: 'a', size: 2 } + +function MyType(size, value) { + this.value = value + this.size = size +} + +function mytipeEncode(obj) { + var buf = new Buffer(obj.size) + buf.fill(obj.value) + return buf +} + +function mytipeDecode(data) { + var result = new MyType(data.length, data.toString('utf8', 0, 1)) + , i + + for (i = 0; i < data.length; i++) { + if (data.readUInt8(0) != data.readUInt8(i)) { + throw new Error('should all be the same') + } + } + + return result +} +``` + + +# 참조 + +- [MessagePack](http://msgpack.org/index.html) +- [MsgPack vs. JSON: Cut your client-server exchange traffic by 50% with one line of code](http://indiegamr.com/cut-your-data-exchange-traffic-by-up-to-50-with-one-line-of-code-msgpack-vs-json/) diff --git a/ko/miniscript.md b/ko/miniscript.md new file mode 100644 index 0000000000..8c68f0e6e7 --- /dev/null +++ b/ko/miniscript.md @@ -0,0 +1,413 @@ +--- +name: MiniScript +contributors: + - ["Joe Strout", "https://github.com/JoeStrout"] +filename: miniscript.ms +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +**MiniScript**는 게임 및 기타 소프트웨어에 쉽게 내장되도록 설계된 간단한 스크립팅 언어입니다. 명령줄에서 사용하거나 [Soda](https://github.com/JoeStrout/soda) 또는 [Mini Micro](https://miniscript.org/MiniMicro)를 통해 크로스 플랫폼 게임 개발 환경으로 사용할 수도 있습니다. + +MiniScript를 시작하는 쉬운 방법은 서버에서 MiniScript 코드를 실행하는 [Try-It! 페이지](https://miniscript.org/tryit/)를 이용하는 것입니다. 그러나 이 페이지의 코드는 2000자로 제한됩니다. (아래 튜토리얼 스크립트는 Try-It! 페이지에서 실행되도록 2048자 이하의 블록으로 나뉘어 있습니다.) + +Try-It! 페이지를 넘어설 준비가 되면, 다음 단계는 명령줄과 프로그램 모두에서 MiniScript를 사용하는 무료 가상 컴퓨터인 [Mini Micro](https://miniscript.org/MiniMicro)를 다운로드하는 것입니다. 해당 환경에서 프롬프트에 **edit**를 입력하여 코드를 편집한 다음, 편집기에서 실행 버튼을 클릭하여 실행하십시오. + +``` +print "Hello world" + +// MiniScript는 매우 구문이 가볍습니다. 위 print 문에 괄호가 +// 필요하지 않다는 점에 유의하십시오. 주석은 //로 시작하여 +// 줄 끝까지 이어집니다. MiniScript는 대소문자를 구분합니다. + +// 흐름 제어 +// 어떤 조건에 따라 다른 작업을 수행하려면 if 블록을 사용하십시오. +// 0개 이상의 else if 블록과 선택적인 else 블록 하나를 포함할 수 있습니다. +if 2+2 == 4 then + print "수학이 작동합니다!" +else if pi > 3 then + print "파이는 맛있습니다" +else if "a" < "b" then + print "정렬할 수 있습니다" +else + print "마지막 기회" +end if + +// 반복 +// MiniScript에는 while 루프와 for 루프 두 가지 반복 구문만 있습니다. +// 조건이 참인 동안 반복하려면 while 블록을 사용하십시오. +s = "Spam" +while s.len < 50 + s = s + ", spam" +end while +print s + " and spam!" + +// for 루프는 range 함수로 쉽게 만들 수 있는 리스트를 포함하여 +// 모든 리스트를 반복할 수 있습니다. +for i in range(10, 1) + print i + "..." +end for +print "발사!" + +// 루프 내에서 유용한 두 가지 추가 키워드가 있습니다. break 문은 +// 가장 가까운 while 또는 for 루프를 빠져나옵니다. continue 문은 +// 현재 반복의 나머지 부분을 건너뛰고 루프의 맨 위로 점프합니다. +for i in range(1,100) + if i % 3 == 0 then continue // 3의 배수 건너뛰기 + if i^2 > 200 then break // i^2이 200을 초과하면 중지 + print i + "의 제곱은 " + i^2 +end for +``` + +### 숫자 + +``` +// 모든 숫자는 전체 정밀도 형식으로 저장됩니다. 숫자는 또한 +// 참(1)과 거짓(0)을 나타내며, 이에 대한 내장 키워드 +// (true 및 false)가 있습니다. +a = 7 +b = 3 +ultimateAnswer = 42 +pi = 3.14159 +n = true +m = false +print ultimateAnswer + ", " + pi + ", " + n + ", " + m + +// 숫자는 다음 연산자를 지원합니다: +print "기본 수학:" +print a + b // 덧셈 +print a - b // 뺄셈 +print a * b // 곱셈 +print a / b // 나눗셈 +print a % b // 모듈로 (나머지) +print a ^ b // 거듭제곱 + +print "논리:" +print n and m // 논리 "and" +print n or m // 논리 "or" +print not n // 논리 부정 + +print "비교:" +print a == b // 동등성 테스트 (여기서는 =가 아닌 ==에 유의!) +print a != b // 부등 +print a > b // 보다 큼 +print a >= b // 보다 크거나 같음 +print a < b // 보다 작음 +print a <= b // 보다 작거나 같음 +``` + +### 문자열 + +``` +// 텍스트는 유니코드 문자의 문자열로 저장됩니다. 따옴표로 +// 둘러싸서 문자열을 작성합니다. 문자열에 인용 부호를 포함해야 +// 하는 경우 두 번 입력하십시오. +print "Hello, ""Bob""." +a = "Hello" +b = "Spam" + +// 문자열은 다음 연산자를 지원합니다: +print "문자열 ""수학"":" +print a + b // 문자열 연결 +print b - "m" // 문자열 빼기 (자르기) +print b * 4 // 문자열 복제 +print a / 2 // 문자열 나누기 + +print "비교:" +print a == b // 동등성 테스트 (여기서는 =가 아닌 ==에 유의!) +print a != b // 부등 +print a > b // 보다 큼 +print a >= b // 보다 크거나 같음 +print a < b // 보다 작음 +print a <= b // 보다 작거나 같음 + +// 문자열의 인덱싱 및 슬라이싱은 대괄호 안에 인덱스(또는 두 개)를 +// 사용하여 수행됩니다. 앞에서부터 세려면 0부터 시작하는 인덱스를 사용하고, +// 뒤에서부터 세려면 음수 인덱스를 사용합니다. 콜론으로 구분된 +// 두 개의 인덱스로 슬라이스(부분 문자열)를 가져옵니다. 둘 중 하나를 생략하여 +// 슬라이스를 문자열의 시작 또는 끝까지 확장할 수 있습니다. +print "인덱싱 및 슬라이싱:" +print a[0] // 0부터 시작하는 문자 가져오기 ("H") +print a[1] // 두 번째 문자 가져오기 ("e") +print a[-1] // 음수는 뒤에서부터 셉니다 ("o") +print a[1:4] // 1부터 4 미만까지 슬라이스 가져오기 ("ell") +print a[1:-1] // 위와 같지만 음수 인덱스 사용 +print a[1:] // 1부터 끝까지 슬라이스 가져오기 ("ello") +print a[:2] // 처음부터 2 미만까지 슬라이스 가져오기 ("He") + +// MiniScript의 문자열은 불변입니다. 문자열에 접근하여 +// 포함된 문자를 변경할 수는 없지만(항상 다른 문자로 +// 새 문자열을 만들 수는 있습니다). +``` + +### 리스트 + +``` +// 리스트는 모든 유형의 값으로 구성된 순서 있는 시퀀스입니다. +// for 루프로 리스트를 반복하거나 .indexes를 사용하여 +// 인덱스를 반복할 수 있습니다. +x = ["alpha", "beta", "gamma", "delta"] +for item in x + print item +end for +for i in x.indexes + print "x[" + i + "] is " + x[i] +end for + +// 리스트의 인덱싱 및 슬라이싱은 문자열과 정확히 같습니다. +// 앞에서부터 세려면 0부터 시작하는 인덱스를 사용하고, 뒤에서부터 +// 세려면 음수를 사용합니다. 콜론으로 구분된 두 개의 인덱스로 +// 리스트의 슬라이스(부분 집합)를 가져옵니다. 둘 중 하나를 생략하여 +// 슬라이스를 리스트의 시작 또는 끝까지 확장할 수 있습니다. +print x[0] // alpha +print x[-1] // delta +print x[1:3] // [beta, gamma] +print x[2:] // [gamma, delta] +print x[:-1] // [alpha, beta, gamma] + +// 리스트는 다음 연산자를 지원합니다: +y = ["a", "be", "ce", "de"] +print "리스트 ""수학"":" +print x + y // 리스트 연결 +print y * 3 // 리스트 복제 +print x / 2 // 리스트 나누기 + +print "비교:" +print x == y // 동등성 테스트 (여기서는 =가 아닌 ==에 유의!) +print x != y // 부등 +``` + +### 맵 + +``` +// 맵은 고유한 키와 연관된 값의 집합입니다. 맵은 +// 데이터 레코드, 객체, 희소 배열 등을 나타내는 데 사용되는 +// 매우 강력하고 다재다능한 데이터 유형입니다. +// 중괄호로 맵을 만들고 대괄호로 단일 값을 가져오거나 +// 설정합니다. 키와 값은 모든 유형이 될 수 있습니다. +// (맵의 컨텍스트에서 "키"와 "인덱스"는 같은 의미입니다.) +m = {1:"one", 2:"two"} +print m[1] // one +m[2] = "dos" // 인덱스 2와 연관된 값 변경 +print m[2] // dos + +// 키(인덱스)가 유효한 변수 이름이 되는 문자열인 특수한 경우, +// 대괄호 구문 대신 점 구문이 있습니다. 맵과 점(마침표) 뒤에 +// 따옴표 없이 키를 넣으십시오. +m.pi = 3.14 // m["pi"] = 3.14와 동일 +print m["pi"] // 3.14 +m["e"] = 2.72 // m.e = 2.72와 동일 +print m.e // 2.72 + +// 맵은 + 연산자만 지원하며, 두 맵의 모든 키/값 쌍을 +// 하나로 결합합니다. +m1 = {1:"one", 2:"two"} +m2 = {2:"dos", 3:"tres"} +print m1 + m2 // 맵 연결 + +// for 루프로 맵의 키/값 쌍을 반복할 수 있습니다. +// 각 반복에서 변수는 "key"와 "value" 인덱스가 있는 +// 작은 맵이 됩니다. +for kv in m1+m2 + print kv.key + " -> " + kv.value +end for + +// 맵의 키/값 쌍 순서는 정의되지 않습니다. +// 맵을 인쇄하거나 반복할 때 특정 순서로 나타날 것이라고 +// 가정해서는 안 됩니다. +``` + +### 함수 + +``` +// miniscript에서 함수는 function...end function 블록으로 만듭니다. +// 대부분의 경우 나중에 호출할 수 있도록 결과를 변수에 할당합니다. +// 함수가 결과를 반환해야 하는 경우 return 키워드로 수행합니다. +rollDie = function + return ceil(rnd * 6) // 1-6 사이의 난수 반환 +end function +print rollDie +print rollDie + +// 매개변수가 필요한 경우 function 키워드 뒤에 괄호 안에 넣습니다. +// 매개변수는 기본값을 가질 수 있습니다. +roll = function(numberOfDice, sides=6) + sum = 0 + for i in range(1, numberOfDice) + sum = sum + ceil(rnd * sides) + end for + return sum +end function +print roll(2) // 6면체 주사위 2개 굴리기 +print roll(2,20) // 20면체 주사위 2개 굴리기 + +// 변수는 MiniScript에서 기본적으로 항상 지역 변수입니다. +// 위 함수의 변수 i와 sum은 함수 외부에서 액세스할 수 없으며 +// 함수가 반환되는 즉시 사라집니다. (변수 범위에 대해서는 나중에 자세히 설명합니다.) + +// 괄호는 (1) 함수에 인수(매개변수 값)를 전달하고 있고 +// (2) 결과를 더 큰 문의 일부로 사용하는 경우에만 필요합니다. +// 위의 첫 번째 예제에서 rollDie는 인수를 전달하지 않았기 때문에 +// 괄호가 필요하지 않았습니다. 다음은 내장 print 함수와 같이 +// 그 자체로 문으로 사용되어 괄호가 필요하지 않은 함수의 예입니다. +doRoll = function(numberOfDice, sides=6) + print "Rolling " + numberOfDice + "d" + sides + "..." + sum = 0 + for i in range(1, numberOfDice) + roll = ceil(rnd * sides) + print "You rolled a " + roll + "." + sum = sum + roll + end for + print "Your total is: " + sum +end function +doRoll 3 // 3d6 굴리기 -- 괄호 필요 없음 +doRoll 3, 8 // 여기도 마찬가지지만 3d8 굴리기 + +// 함수를 호출하지 않고 참조해야 하는 경우 @ 연산자를 사용할 수 있습니다. +f = @doRoll // f가 doRoll과 동일한 함수를 참조하도록 함 +f 2,4 // 2d4 굴리기 +``` + +### 클래스와 객체 + +``` +// MiniScript는 프로토타입 기반 상속을 사용합니다. 클래스나 객체는 +// 부모 클래스를 가리키는 특수 __isa 항목이 있는 맵일 뿐입니다. +// 이것은 new 연산자를 사용할 때 자동으로 설정됩니다. +Shape = {} // 기본 클래스 만들기 +Shape.sides = 0 // 기본적으로 0개의 변을 가짐 + +Square = new Shape // Shape의 서브클래스 Square 만들기 +Square.sides = 4 // 변의 수 재정의 + +x = new Square // Square 클래스의 인스턴스 만들기 +print x.sides // 4, x는 Square이고 Square.sides는 4이므로 + +// 메서드는 클래스(맵)에 저장된 함수일 뿐입니다. 이들은 +// 다른 값과 마찬가지로 __isa 체인을 통해 상속됩니다. +// 메서드 내에서 self 키워드는 메서드가 호출된 객체(점 구문 사용)를 +// 참조합니다. 이것이 객체의 데이터나 메서드를 참조하는 방법입니다. +Shape.describe = function + print + print "This is a " + self.sides + "-sided shape." +end function +x.describe // This is a 4-sided shape. + +// 메서드는 (다시 값처럼) 재정의될 수 있습니다. 서브클래스/인스턴스 +// 메서드에서 super를 사용하여 상속 체인의 다음 버전 메서드를 +// 호출하면서도 self를 이 메서드가 호출된 객체에 바인딩된 상태로 +// 유지할 수 있습니다. +Square.describe = function + super.describe // 먼저 표준 설명 수행 + print "It looks very squarish." // 그런 다음 이것을 추가 +end function +x.describe +``` + +### 변수 범위에 대한 추가 정보 + +``` +// MiniScript의 변수 할당은 항상 지역 변수, 즉 +// 할당을 포함하는 함수 내에서만 존재하는 변수를 만들거나 +// 업데이트합니다. 단, 점 구문을 사용하여 다른 범위를 지정하는 경우는 +// 예외입니다. +x = 42 // x라는 전역 변수 +f = function + x = 1234 // x라는 지역 변수 만들기 + print "Inside the function, x is now " + x +end function +f +print "Outside the function, x is " + x + +// 위 예에서 함수 내의 x에 대한 할당은 +// 우연히 같은 이름을 가졌음에도 불구하고 전역 x 값에 +// 영향을 미치지 않습니다. (이것은 의도하지 않은 부작용을 +// 피하는 데 도움이 되므로 좋은 점입니다.) 전역 변수는 +// 일반적으로 권장되지 않지만, 함수 내에서 업데이트해야 하는 경우 +// "globals." 접두사를 사용하여 그렇게 할 수 있습니다. +f = function + print "Using the globals prefix..." + globals.x = 1234 // 전역 변수 x 업데이트 + print "Inside the function, x is now " + x +end function +f +print "Outside the function, x is " + x + +// 이것은 클래스 메서드와 함께 사용되는 "self." 접두사와 매우 유사합니다. +// 두 경우 모두 변수에 더 구체적인 범위를 제공합니다(실제로는 +// 점 구문으로 인덱싱할 맵을 지정하는 것일 뿐입니다). + +// 그러나 중요한 차이점이 있습니다. 변수를 할당하는 것이 아니라 +// 읽을 때, 변수 이름이 지역 변수 중에서 발견되지 않으면 +// MiniScript는 자동으로 해당 이름의 전역 변수를 찾습니다. +// 따라서 변수를 읽을 때는 "globals." 접두사가 필요하지 않지만 +// 할당할 때는 필요합니다. +count = 0 +addToCount = function(amount=1) + globals.count = count + amount +end function +addToCount +addToCount +print "count is now: " + count + +// 위 addToCount 함수에서 할당의 왼쪽에는 globals 접두사가 +// 필요한 반면, 오른쪽에는 전역 값을 읽기만 하므로 +// 필요하지 않다는 점에 유의하십시오. +``` + +### 유용한 내장 메서드 + +``` +// 내장 메서드는 MiniScript나 그 환경에 내장된 메서드입니다. +// 특정 MiniScript 환경(예: Mini Micro, Soda, 명령줄 MiniScript, +// MiniScript를 내장 언어로 사용하는 일부 게임 등)은 아마도 +// 추가 내장 함수를 추가할 것입니다. 그러나 항상 사용할 수 있어야 하는 +// 약 50개의 핵심 내장 함수가 있습니다. + +// 가장 일반적으로 사용되는 몇 가지에 대한 빠른 데모입니다. +print abs(-42) // 절대값 +print pi // 파이 값 가져오기 (예, 내장되어 있습니다!) +print cos(pi) // 코사인 +print sqrt(100) // 제곱근 +print round(pi, 2) // 반올림 (소수점 2자리까지) +print char(65) // 유니코드 문자 65 가져오기 + +print +s = "Hello world!" +print s.upper // 대문자로 변환 +print s.len // 길이 가져오기 (문자 수) +print s.replace("Hello", "Heya") // 문자열 대체 +print s.split(" ") // 공백으로 분할하여 리스트 만들기 +print s.remove("l") // "l"의 첫 번째 항목 제거 + +print +a = range(2,15,3) // 리스트 만들기: 2부터 15까지, 3단계씩 +print "a: " + a +print "a.len:" + a.len // 길이 가져오기 (값 수) +print "a.sum:" + a.sum // 모든 값을 더한 합계 가져오기 +print a.pop // 마지막 값 꺼내기 +print a.pull // 첫 번째 값 꺼내기 +print "popped and pulled: " + a +a.push 99 // 끝에 새 항목 추가 +a.insert 0, 101 // 인덱스 0에 새 항목 삽입 +print "after push and insert: " + a +a.remove 2 // 리스트에서 인덱스 2 제거 +print "after remove 2: " + a +s = a.join("#") // #으로 값을 결합하여 문자열 만들기 +print s + +print +m = {"one": "uno", "two": "dos", "three": "tres"} +print m.hasIndex("one") // 키 존재 여부 확인 +print m.indexes // 모든 인덱스 가져오기 +print m.values // 모든 값 가져오기 +m.remove "two" // 인덱스 (및 해당 값) 제거 +print m +``` + +## 더 읽을거리 + +* [MiniScript.org 웹사이트](https://miniscript.org/) — MiniScript 세계의 중심 +* [MiniScript 빠른 참조](https://miniscript.org/files/MiniScript-QuickRef.pdf) — 이 튜토리얼을 한 페이지로 +* [MiniScript 사용자 매뉴얼](https://miniscript.org/files/MiniScript-Manual.pdf) — 더 심층적인 문서 +* [MiniScript 위키](https://miniscript.org/wiki/) — 커뮤니티 기반 문서 diff --git a/ko/mips.md b/ko/mips.md new file mode 100644 index 0000000000..7092281054 --- /dev/null +++ b/ko/mips.md @@ -0,0 +1,367 @@ +--- +name: "MIPS Assembly" +filename: MIPS.asm +contributors: + - ["Stanley Lim", "https://github.com/Spiderpig86"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +MIPS(Microprocessor without Interlocked Pipeline Stages) 어셈블리 언어는 1981년 J. L. Hennessy가 설계한 MIPS 마이크로프로세서 패러다임과 함께 작동하도록 설계되었습니다. 이러한 RISC 프로세서는 게이트웨이 및 라우터와 같은 임베디드 시스템에 사용됩니다. + +[더 읽어보기](https://en.wikipedia.org/wiki/MIPS_architecture) + +```asm +# 주석은 '#'으로 표시됩니다. + +# '#' 뒤에 오는 모든 것은 어셈블러의 렉서에 의해 무시됩니다. + +# 프로그램은 일반적으로 .data와 .text 섹션을 포함합니다. + +.data # 데이터가 메모리에 저장되는 섹션(RAM에 할당됨), 상위 수준 언어의 + # 변수와 유사합니다. + + # 선언은 (레이블: .타입 값) 형식의 선언을 따릅니다. + hello_world: .asciiz "Hello World\n" # null로 끝나는 문자열 선언 + num1: .word 42 # 정수는 워드(32비트 값)라고 합니다. + + arr1: .word 1, 2, 3, 4, 5 # 워드 배열 + arr2: .byte 'a', 'b' # 문자 배열 (각 1바이트) + buffer: .space 60 # RAM에 공간을 할당합니다. + # (0으로 지워지지 않음) + + # 데이터 타입 크기 + _byte: .byte 'a' # 1바이트 + _halfword: .half 53 # 2바이트 + _word: .word 3 # 4바이트 + _float: .float 3.14 # 4바이트 + _double: .double 7.0 # 8바이트 + + .align 2 # 데이터의 메모리 정렬, 여기서 + # 숫자는 2의 거듭제곱으로 + # 바이트 정렬을 나타냅니다. + # (.align 2는 2^2 = 4바이트이므로 + # 워드 정렬을 나타냅니다) + +.text # 명령어와 프로그램 로직을 + # 포함하는 섹션 +.globl _main # 명령어 레이블을 전역으로 선언하여 + # 다른 파일에서 접근할 수 있도록 합니다. + + _main: # MIPS 프로그램은 명령어를 + # 순차적으로 실행하며, 이 레이블 + # 아래의 코드가 먼저 실행됩니다. + + # "hello world"를 출력해 봅시다 + la $a0, hello_world # 메모리에 저장된 문자열의 + # 주소 로드 + li $v0, 4 # 시스템 호출 값 로드 (어떤 + # 시스템 호출을 할지 나타내는 숫자) + syscall # 주어진 인수($a0)로 지정된 + # 시스템 호출 수행 + + # 레지스터 (프로그램 실행 중 데이터 보관에 사용) + # $t0 - $t9 # 서브루틴 내부의 중간 계산에 + # 사용되는 임시 레지스터 + # (함수 호출 간에 저장되지 않음) + + # $s0 - $s7 # 서브루틴 호출 간에 값이 저장되는 + # 저장 레지스터. + # 일반적으로 스택에 저장됨 + + # $a0 - $a3 # 서브루틴에 인수를 전달하기 위한 + # 인수 레지스터 + # $v0 - $v1 # 호출자 함수에 값을 반환하기 위한 + # 반환 레지스터 + + # 로드/저장 명령어 유형 + la $t0, label # 레이블로 지정된 메모리의 값의 + # 주소를 레지스터 $t0에 복사 + lw $t0, label # 메모리에서 워드 값 복사 + lw $t1, 4($s0) # 4바이트 오프셋이 있는 레지스터에 + # 저장된 주소에서 워드 값 복사 + # (addr + 4) + lb $t2, label # 바이트 값을 레지스터 $t2의 + # 하위 부분에 복사 + lb $t2, 0($s0) # $s0의 소스 주소에서 오프셋 0으로 + # 바이트 값 복사 + # 하프워드의 경우 'lh'와 동일한 아이디어 + + sw $t0, label # 레이블로 매핑된 메모리 주소에 + # 워드 값 저장 + sw $t0, 8($s0) # $s0에 지정된 주소와 8바이트 + # 오프셋에 워드 값 저장 + # 바이트와 하프워드의 경우 'sb'와 'sh'를 사용하는 동일한 아이디어. 'sa'는 존재하지 않음 + +### 수학 ### + _math: + # 레지스터에 값을 로드하는 것을 잊지 마십시오 + lw $t0, num # 데이터 섹션에서 + li $t0, 5 # 또는 즉시(상수)에서 + li $t1, 6 + add $t2, $t0, $t1 # $t2 = $t0 + $t1 + sub $t2, $t0, $t1 # $t2 = $t0 - $t1 + mul $t2, $t0, $t1 # $t2 = $t0 * $t1 + div $t2, $t0, $t1 # $t2 = $t0 / $t1 (일부 MARS + # 버전에서는 지원되지 않을 수 있음) + div $t0, $t1 # $t0 / $t1을 수행합니다. 'mflo'를 + # 사용하여 몫을, 'mfhi'를 사용하여 + # 나머지를 가져옵니다 + + # 비트 시프트 + sll $t0, $t0, 2 # 2의 즉시(상수 값)로 + # 왼쪽으로 비트 시프트 + sllv $t0, $t1, $t2 # 레지스터의 변수 양만큼 + # 왼쪽으로 시프트 + srl $t0, $t0, 5 # 오른쪽으로 비트 시프트 (부호를 + # 보존하지 않음, 0으로 부호 확장) + srlv $t0, $t1, $t2 # 레지스터의 변수 양만큼 + # 오른쪽으로 시프트 + sra $t0, $t0, 7 # 오른쪽으로 산술 비트 시프트 + # (부호 보존) + srav $t0, $t1, $t2 # 레지스터의 변수 양만큼 + # 오른쪽으로 시프트 + + # 비트 연산자 + and $t0, $t1, $t2 # 비트 AND + andi $t0, $t1, 0xFFF # 즉시와 비트 AND + or $t0, $t1, $t2 # 비트 OR + ori $t0, $t1, 0xFFF # 즉시와 비트 OR + xor $t0, $t1, $t2 # 비트 XOR + xori $t0, $t1, 0xFFF # 즉시와 비트 XOR + nor $t0, $t1, $t2 # 비트 NOR + +## 분기 ## + _branching: + # 이러한 분기 명령어의 기본 형식은 일반적으로 + # + + + +//- JS 및 CSS 가져오기 +script + include scripts/index.js +style + include styles/theme.css + +//- ---믹스인--- +mixin basic + div Hello ++basic +//-
Hello
+ +mixin comment(name, comment) + div + span.comment-name= name + div.comment-text= comment ++comment("Bob", "This is Awesome") +//- +
+ Bob +
This is Awesome
+
+``` + +### 추가 자료 + +- [사이트](https://pugjs.org/) +- [문서](https://pugjs.org/api/getting-started.html) +- [GitHub 저장소](https://github.com/pugjs/pug) diff --git a/ko/purescript.md b/ko/purescript.md new file mode 100644 index 0000000000..971a578458 --- /dev/null +++ b/ko/purescript.md @@ -0,0 +1,209 @@ +--- +name: PureScript +filename: purescript.purs +contributors: + - ["Fredrik Dyrkell", "http://www.lexicallyscoped.com"] + - ["Thimoteus", "https://github.com/Thimoteus"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +PureScript는 JavaScript로 컴파일되는 작고 강력하며 정적으로 유형이 지정된 언어입니다. + +* 자세히 알아보기: [https://www.purescript.org/](https://www.purescript.org/) +* 문서: [https://pursuit.purescript.org/](https://pursuit.purescript.org/) +* 책: Purescript by Example, [https://book.purescript.org/](https://book.purescript.org/) + +주석 처리되지 않은 모든 코드 줄은 PSCi REPL에서 실행할 수 있지만, 일부는 +"붙여넣기" 모드(`:paste` 다음에 여러 줄을 입력하고 ^D로 종료)가 +필요합니다. + +```haskell +-- +-- 1. 런타임에 JavaScript와 동등한 기본 데이터 유형입니다. + +import Prelude +-- 숫자 +1.0 + 7.2*5.5 :: Number -- 40.6 +-- 정수 +1 + 2*5 :: Int -- 11 +-- 유형이 추론되므로 다음은 잘 작동합니다 +9.0/2.5 + 4.4 -- 8.0 +-- 그러나 Int와 Number는 혼합되지 않으므로 다음은 작동하지 않습니다 +5/2 + 2.5 -- 표현식 2.5에 Int 유형이 없습니다 +-- 16진수 리터럴 +0xff + 1 -- 256 +-- 단항 부정 +6 * -3 -- -18 +6 * negate 3 -- -18 +-- 모듈러스, purescript-math (Math)에서 +3.0 % 2.0 -- 1.0 +4.0 % 2.0 -- 0.0 +-- psci에서 표현식의 유형 검사 +:t 9.5/2.5 + 4.4 -- Number + +-- 불리언 +true :: Boolean -- true +false :: Boolean -- false +-- 부정 +not true -- false +23 == 23 -- true +1 /= 4 -- true +1 >= 4 -- false +-- 비교 < <= > >= +-- compare로 정의됨 +compare 1 2 -- LT +compare 2 2 -- EQ +compare 3 2 -- GT +-- 논리곱 및 논리합 +true && (9 >= 19 || 1 < 2) -- true + +-- 문자열 +"Hello" :: String -- "Hello" +-- 개행 없는 여러 줄 문자열, PSCi에서 실행하려면 "paste" 모드 사용 +"Hello\ +\orld" -- "Helloworld" +-- 개행 있는 여러 줄 문자열 +"""Hello +world""" -- "Hello\nworld" +-- 연결 +"such " <> "amaze" -- "such amaze" + +-- +-- 2. 배열은 JavaScript 배열이지만 동종이어야 합니다. + +[1,1,2,3,5,8] :: Array Int -- [1,1,2,3,5,8] +[1.2,2.0,3.14] :: Array Number -- [1.2,2.0,3.14] +[true, true, false] :: Array Boolean -- [true,true,false] +-- [1,2, true, "false"]는 작동하지 않음 +-- `Int를 Boolean과 통합할 수 없음` + +-- purescript-arrays (Data.Array) 필요 +-- Cons (앞에 추가) +1 : [2,4,3] -- [1,2,4,3] + +-- 및 purescript-maybe (Data.Maybe) +-- 안전한 액세스는 Maybe a를 반환 +head [1,2,3] -- (Just 1) +tail [3,2,1] -- (Just [2,1]) +init [1,2,3] -- (Just [1,2]) +last [3,2,1] -- (Just 1) +-- 배열 액세스 - 인덱싱 +[3,4,5,6,7] !! 2 -- (Just 5) +-- 범위 +1..5 -- [1,2,3,4,5] +length [2,2,2] -- 3 +drop 3 [5,4,3,2,1] -- [2,1] +take 3 [5,4,3,2,1] -- [5,4,3] +append [1,2,3] [4,5,6] -- [1,2,3,4,5,6] + +-- +-- 3. 레코드는 0개 이상의 필드가 있는 JavaScript 객체이며, +-- 다른 유형을 가질 수 있습니다. +book = {title: "Foucault's pendulum", author: "Umberto Eco"} +-- 속성 액세스 +book.title -- "Foucault's pendulum" + +getTitle b = b.title +-- 제목이 있는 모든 레코드에서 작동 (다른 필드는 필요하지 않음) +getTitle book -- "Foucault's pendulum" +getTitle {title: "Weekend in Monaco", artist: "The Rippingtons"} -- "Weekend in Monaco" +-- 약식으로 밑줄 사용 가능 +_.title book -- "Foucault's pendulum" +-- 레코드 업데이트 +changeTitle b t = b {title = t} +getTitle (changeTitle book "Ill nome della rosa") -- "Ill nome della rosa" + +-- +-- 4. 함수 +-- PSCi의 붙여넣기 모드에서 +sumOfSquares :: Int -> Int -> Int +sumOfSquares x y = x*x + y*y +sumOfSquares 3 4 -- 25 + +myMod x y = x % y +myMod 3.0 2.0 -- 1.0 +-- 함수의 중위 적용 +3 `mod` 2 -- 1 + +-- 함수 적용은 다른 모든 연산자보다 우선순위가 높습니다 +sumOfSquares 3 4 * sumOfSquares 4 5 -- 1025 + +-- 조건부 +abs' n = if n>=0 then n else -n +abs' (-3) -- 3 + +-- 가드된 방정식 +-- PSCi의 붙여넣기 모드에서 +abs'' n | n >= 0 = n + | otherwise = -n + +-- 패턴 매칭 + +-- 유형 서명을 참고하십시오. 입력은 숫자 목록입니다. 패턴 매칭은 +-- 목록을 분해하고 부분을 바인딩합니다. +-- purescript-lists (Data.List) 및 purescript-maybe (Data.Maybe) 필요 +first :: forall a. List a -> Maybe a +first (x : _) = Just x +first Nil = Nothing +first (fromFoldable [3,4,5]) -- (Just 3) + +second :: forall a. List a -> Maybe a +second Nil = Nothing +second (_ : Nil) = Nothing +second (_ : (y : _)) = Just y +second (fromFoldable [3,4,5]) -- (Just 4) + +-- 일치시킬 보완 패턴 +-- 좋은 옛 피보나치 +fib 1 = 1 +fib 2 = 2 +fib x = fib (x-1) + fib (x-2) +fib 10 -- 89 + +-- 바인딩 이름에 신경 쓰지 않는 경우 밑줄을 사용하여 무엇이든 일치시킵니다 +isZero 0 = true +isZero _ = false +isZero 9 -- false + +-- 레코드에 대한 패턴 매칭 +ecoTitle {author: "Umberto Eco", title: t} = Just t +ecoTitle _ = Nothing + +ecoTitle {title: "Foucault's pendulum", author: "Umberto Eco"} -- (Just "Foucault's pendulum") +ecoTitle {title: "The Quantum Thief", author: "Hannu Rajaniemi"} -- Nothing +-- ecoTitle은 유형 검사를 위해 두 필드 모두 필요: +ecoTitle {title: "The Quantum Thief"} -- 객체에 필요한 속성 "author"가 없음 + +-- 람다 표현식 +(\x -> x*x) 3 -- 9 +(\x y -> x*x + y*y) 4 5 -- 41 +sqr = \x -> x*x + +-- 커링 +myAdd x y = x + y -- 와 동일 +myAdd' = \x -> \y -> x + y +add3 = myAdd 3 +:t add3 -- Int -> Int + +-- 순방향 및 역방향 함수 구성 +-- drop 3 다음에 take 5 +(drop 3 >>> take 5) (1..20) -- [4,5,6,7,8] +-- take 5 다음에 drop 3 +(drop 3 <<< take 5) (1..20) -- [4,5] + +-- 고차 함수를 사용한 연산 +even x = x `mod` 2 == 0 +filter even (1..10) -- [2,4,6,8,10] +map (\x -> x + 11) (1..5) -- [12,13,14,15,16] + +-- purescript-foldable-traversable (Data.Foldable) 필요 + +foldr (+) 0 (1..10) -- 55 +sum (1..10) -- 55 +product (1..10) -- 3628800 + +-- 술어로 테스트 +any even [1,2,3] -- true +all even [1,2,3] -- false +``` diff --git a/ko/pyqt.md b/ko/pyqt.md new file mode 100644 index 0000000000..514225c0a3 --- /dev/null +++ b/ko/pyqt.md @@ -0,0 +1,81 @@ +--- +category: framework +name: PyQt +filename: learnpyqt.py +contributors: + - ["Nathan Hughes", "https://github.com/sirsharpest"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +**Qt**는 코드 변경이 거의 또는 전혀 없이 다양한 소프트웨어 및 하드웨어 플랫폼에서 실행할 수 있는 크로스 플랫폼 소프트웨어를 개발하기 위한 널리 알려진 프레임워크이며, 네이티브 애플리케이션의 성능과 속도를 가지고 있습니다. **Qt**는 원래 *C++*로 작성되었습니다. + + +이것은 [Aleksey Kholovchuk](https://github.com/vortexxx192)의 C++ QT 소개를 각색한 것으로, 일부 코드 예제는 동일한 기능을 가져야 하며 이 버전은 pyqt를 사용하여 작성되었습니다! + +```python +import sys +from PyQt4 import QtGui + +def window(): + # 애플리케이션 객체 생성 + app = QtGui.QApplication(sys.argv) + # 레이블이 배치될 위젯 생성 + w = QtGui.QWidget() + # 위젯에 레이블 추가 + b = QtGui.QLabel(w) + # 레이블에 텍스트 설정 + b.setText("Hello World!") + # 크기 및 배치 정보 제공 + w.setGeometry(100, 100, 200, 50) + b.move(50, 20) + # 창에 멋진 제목 부여 + w.setWindowTitle("PyQt") + # 모든 것을 표시 + w.show() + # 모든 설정이 완료되면 요청한 내용 실행 + sys.exit(app.exec_()) + +if __name__ == '__main__': + window() +``` + +**pyqt**에서 더 고급 기능을 사용하려면 추가 요소를 구축하는 것을 고려해야 합니다. +여기서는 사용자에게 결정 확인을 요청하거나 정보를 제공하는 데 유용한 대화 상자 팝업 상자를 도입하는 방법을 보여줍니다. + +```python +import sys +from PyQt4.QtGui import * +from PyQt4.QtCore import * + + +def window(): + app = QApplication(sys.argv) + w = QWidget() + # 버튼을 생성하고 위젯 w에 연결 + b = QPushButton(w) + b.setText("Press me") + b.move(50, 50) + # b를 클릭했을 때 이 함수를 호출하도록 지시 + # 함수 호출에 "()"가 없는 점에 유의 + b.clicked.connect(showdialog) + w.setWindowTitle("PyQt Dialog") + w.show() + sys.exit(app.exec_()) + +# 이 함수는 버튼이 있는 대화 상자 창을 생성해야 하며, +# 버튼을 클릭하면 프로그램이 종료됩니다 +def showdialog(): + d = QDialog() + b1 = QPushButton("ok", d) + b1.move(50, 50) + d.setWindowTitle("Dialog") + # 이 양식은 팝업이 활성화되어 있는 동안 부모를 차단하도록 지시합니다 + d.setWindowModality(Qt.ApplicationModal) + # 클릭 시 전체 프로세스가 종료되기를 원합니다 + b1.clicked.connect(sys.exit) + d.exec_() + +if __name__ == '__main__': + window() +``` diff --git a/ko/python.md b/ko/python.md new file mode 100644 index 0000000000..48fbef51e6 --- /dev/null +++ b/ko/python.md @@ -0,0 +1,1123 @@ +--- +name: Python +contributors: + - ["Louie Dinh", "http://pythonpracticeprojects.com"] + - ["Steven Basart", "http://github.com/xksteven"] + - ["Andre Polykanine", "https://github.com/Oire"] + - ["Zachary Ferguson", "http://github.com/zfergus2"] + - ["evuez", "http://github.com/evuez"] + - ["Rommel Martinez", "https://ebzzry.io"] + - ["Roberto Fernandez Diaz", "https://github.com/robertofd1995"] + - ["caminsha", "https://github.com/caminsha"] + - ["Stanislav Modrak", "https://stanislav.gq"] + - ["John Paul Wohlscheid", "https://gitpi.us"] +filename: learnpython.py +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Python은 90년대 초에 Guido van Rossum에 의해 만들어졌습니다. 지금은 가장 인기 있는 언어 중 하나입니다. 저는 Python의 구문적 명확성에 반했습니다. 기본적으로 실행 가능한 의사 코드입니다. + +```python +# 한 줄 주석은 숫자 기호로 시작합니다. + +""" 여러 줄 문자열은 + 세 개의 "를 사용하여 작성할 수 있으며, 종종 + 문서로 사용됩니다. +""" + +#################################################### +## 1. 기본 데이터 타입 및 연산자 +#################################################### + +# 숫자가 있습니다. +3 # => 3 + +# 수학은 예상대로입니다. +1 + 1 # => 2 +8 - 1 # => 7 +10 * 2 # => 20 +35 / 5 # => 7.0 + +# 내림 나눗셈은 음의 무한대 방향으로 반올림합니다. +5 // 3 # => 1 +-5 // 3 # => -2 +5.0 // 3.0 # => 1.0 # 부동 소수점에서도 작동합니다. +-5.0 // 3.0 # => -2.0 + +# 나눗셈의 결과는 항상 부동 소수점입니다. +10.0 / 3 # => 3.3333333333333335 + +# 나머지 연산 +7 % 3 # => 1 +# i % j는 C와 달리 j와 같은 부호를 가집니다. +-7 % 3 # => 2 + +# 거듭제곱 (x**y, x의 y 제곱) +2**3 # => 8 + +# 괄호로 우선순위 강제 +1 + 3 * 2 # => 7 +(1 + 3) * 2 # => 8 + +# 불리언 값은 기본 타입입니다 (참고: 대소문자 구분) +True # => True +False # => False + +# not으로 부정 +not True # => False +not False # => True + +# 불리언 연산자 +# "and"와 "or"은 대소문자를 구분합니다. +True and False # => False +False or True # => True + +# True와 False는 실제로 1과 0이지만 다른 키워드를 사용합니다. +True + True # => 2 +True * 8 # => 8 +False - 5 # => -5 + +# 비교 연산자는 True와 False의 숫자 값을 봅니다. +0 == False # => True +2 > True # => True +2 == True # => False +-5 != False # => True + +# None, 0, 빈 문자열/리스트/딕셔너리/튜플/세트는 모두 False로 평가됩니다. +# 다른 모든 값은 True입니다. +bool(0) # => False +bool("") # => False +bool([]) # => False +bool({}) # => False +bool(()) # => False +bool(set()) # => False +bool(4) # => True +bool(-6) # => True + +# int에 불리언 논리 연산자를 사용하면 평가를 위해 불리언으로 캐스팅되지만, +# 캐스팅되지 않은 값이 반환됩니다. bool(ints)와 비트 +# and/or (&,|)를 혼동하지 마십시오. +bool(0) # => False +bool(2) # => True +0 and 2 # => 0 +bool(-5) # => True +bool(2) # => True +-5 or 0 # => -5 + +# 같음은 == 입니다. +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 and 2 < 3 # => True +2 < 3 and 3 < 2 # => False +# 연쇄를 사용하면 더 보기 좋습니다. +1 < 2 < 3 # => True +2 < 3 < 2 # => False + +# (is vs. ==) is는 두 변수가 동일한 객체를 참조하는지 확인하지만, ==는 +# 가리키는 객체가 동일한 값을 갖는지 확인합니다. +a = [1, 2, 3, 4] # a를 새 목록 [1, 2, 3, 4]로 지정 +b = a # b를 a가 가리키는 것으로 지정 +b is a # => True, a와 b는 동일한 객체를 참조 +b == a # => True, a와 b의 객체는 동일 +b = [1, 2, 3, 4] # b를 새 목록 [1, 2, 3, 4]로 지정 +b is a # => False, a와 b는 동일한 객체를 참조하지 않음 +b == a # => True, a와 b의 객체는 동일 + +# 문자열은 " 또는 '로 생성됩니다. +"This is a string." +'This is also a string.' + +# 문자열도 추가할 수 있습니다. +"Hello " + "world!" # => "Hello world!" +# 문자열 리터럴(변수는 아님)은 '+'를 사용하지 않고 연결할 수 있습니다. +"Hello " "world!" # => "Hello world!" + +# 문자열은 문자 목록처럼 처리할 수 있습니다. +"Hello world!"[0] # => 'H' + +# 문자열의 길이를 찾을 수 있습니다. +len("This is a string") # => 16 + +# Python 3.6부터 f-문자열 또는 서식화된 문자열 리터럴을 사용할 수 있습니다. +name = "Reiko" +f"She said her name is {name}." # => "She said her name is Reiko" +# 이 중괄호 안의 모든 유효한 Python 표현식은 문자열로 반환됩니다. +f"{name} is {len(name)} characters long." # => "Reiko is 5 characters long." + +# None은 객체입니다. +None # => None + +# 객체를 None과 비교하기 위해 등호 "==" 기호를 사용하지 마십시오. +# 대신 "is"를 사용하십시오. 이것은 객체 ID의 동등성을 확인합니다. +"etc" is None # => False +None is None # => True + +#################################################### +## 2. 변수 및 컬렉션 +#################################################### + +# Python에는 print 함수가 있습니다. +print("I'm Python. Nice to meet you!") # => I'm Python. Nice to meet you! + +# 기본적으로 print 함수는 끝에 줄 바꿈도 출력합니다. +# 끝 문자열을 변경하려면 선택적 인수 end를 사용하십시오. +print("Hello, World", end="!") # => Hello, World! + +# 콘솔에서 입력 데이터를 간단하게 가져오는 방법 +input_string_var = input("Enter some data: ") # 데이터를 문자열로 반환 + +# 선언은 없고 할당만 있습니다. +# 변수 명명 규칙은 snake_case 스타일입니다. +some_var = 5 +some_var # => 5 + +# 이전에 할당되지 않은 변수에 액세스하면 예외가 발생합니다. +# 예외 처리에 대해 자세히 알아보려면 제어 흐름을 참조하십시오. +some_unknown_var # NameError 발생 + +# if는 표현식으로 사용할 수 있습니다. +# C의 '?:' 삼항 연산자와 동일합니다. +"yay!" if 0 > 1 else "nay!" # => "nay!" + +# 리스트는 시퀀스를 저장합니다. +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] # 인덱스 1에서 3까지의 목록 반환 => [2, 4] +li[2:] # 인덱스 2부터 시작하는 목록 반환 => [4, 3] +li[:3] # 처음부터 인덱스 3까지의 목록 반환 => [1, 2, 4] +li[::2] # 2의 단계 크기로 요소를 선택하는 목록 반환 => [1, 4] +li[::-1] # 역순으로 목록 반환 => [3, 4, 2, 1] +# 고급 슬라이스를 만들기 위해 이들의 조합을 사용하십시오. +# li[start:end:step] + +# 슬라이스를 사용하여 한 단계 깊은 복사본 만들기 +li2 = li[:] # => li2 = [1, 2, 4, 3]이지만 (li2 is li)는 false가 됩니다. + +# "del"로 리스트에서 임의의 요소 제거 +del li[2] # li는 이제 [1, 2, 3]입니다. + +# 값의 첫 번째 발생 제거 +li.remove(2) # li는 이제 [1, 3]입니다. +li.remove(2) # 2가 목록에 없으므로 ValueError 발생 + +# 특정 인덱스에 요소 삽입 +li.insert(1, 2) # li는 이제 다시 [1, 2, 3]입니다. + +# 인자와 일치하는 첫 번째 항목의 인덱스 가져오기 +li.index(2) # => 1 +li.index(4) # 4가 목록에 없으므로 ValueError 발생 + +# 리스트를 추가할 수 있습니다. +# 참고: li와 other_li의 값은 수정되지 않습니다. +li + other_li # => [1, 2, 3, 4, 5, 6] + +# "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 발생 + +# 길이가 1인 튜플은 마지막 요소 뒤에 쉼표가 있어야 하지만 +# 0을 포함한 다른 길이의 튜플은 그렇지 않습니다. +type((1)) # => +type((1,)) # => +type(()) # => + +# 튜플에서도 대부분의 리스트 작업을 수행할 수 있습니다. +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입니다. +# 확장 언패킹도 할 수 있습니다. +a, *b, c = (1, 2, 3, 4) # a는 이제 1, b는 이제 [2, 3], c는 이제 4입니다. +# 괄호를 생략하면 기본적으로 튜플이 생성됩니다. +d, e, f = 4, 5, 6 # 튜플 4, 5, 6이 변수 d, e, f로 언패킹됩니다. +# 각각 d = 4, e = 5, f = 6이 되도록 +# 이제 두 값을 바꾸는 것이 얼마나 쉬운지 보십시오. +e, d = d, e # d는 이제 5, e는 이제 4입니다. + + +# 딕셔너리는 키에서 값으로의 매핑을 저장합니다. +empty_dict = {} +# 미리 채워진 딕셔너리 +filled_dict = {"one": 1, "two": 2, "three": 3} + +# 딕셔너리의 키는 불변 타입이어야 합니다. 이는 키가 +# 빠른 조회를 위해 상수 해시 값으로 변환될 수 있도록 하기 위함입니다. +# 불변 타입에는 int, float, string, tuple이 포함됩니다. +invalid_dict = {[1,2,3]: "123"} # => TypeError: unhashable type: 'list' 발생 +valid_dict = {(1,2,3):[1,2,3]} # 값은 모든 타입이 될 수 있습니다. + +# []로 값 조회 +filled_dict["one"] # => 1 + +# "keys()"로 모든 키를 반복 가능한 객체로 가져오기. list()로 호출을 래핑해야 +# 목록으로 변환됩니다. 나중에 이에 대해 이야기하겠습니다. 참고 - Python +# 3.7 미만 버전의 경우 딕셔너리 키 순서가 보장되지 않습니다. 결과가 +# 아래 예제와 정확히 일치하지 않을 수 있습니다. 그러나 Python 3.7부터는 딕셔너리 +# 항목이 딕셔너리에 삽입된 순서를 유지합니다. +list(filled_dict.keys()) # => ["three", "two", "one"] in Python <3.7 +list(filled_dict.keys()) # => ["one", "two", "three"] in Python 3.7+ + + +# "values()"로 모든 값을 반복 가능한 객체로 가져오기. 다시 한 번 list()로 래핑해야 +# 반복 가능한 객체에서 벗어날 수 있습니다. 참고 - 키 순서에 대한 위와 동일합니다. +list(filled_dict.values()) # => [3, 2, 1] in Python <3.7 +list(filled_dict.values()) # => [1, 2, 3] in Python 3.7+ + +# "in"으로 딕셔너리에 키 존재 여부 확인 +"one" in filled_dict # => True +1 in filled_dict # => False + +# 존재하지 않는 키를 조회하면 KeyError입니다. +filled_dict["four"] # KeyError + +# KeyError를 피하기 위해 "get()" 메서드 사용 +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입니다. + +# 딕셔너리에 추가 +filled_dict.update({"four":4}) # => {"one": 1, "two": 2, "three": 3, "four": 4} +filled_dict["four"] = 4 # 딕셔너리에 추가하는 다른 방법 + +# del로 딕셔너리에서 키 제거 +del filled_dict["one"] # filled dict에서 "one" 키 제거 + +# Python 3.5부터 추가 언패킹 옵션을 사용할 수도 있습니다. +{"a": 1, **{"b": 2}} # => {'a': 1, 'b': 2} +{"a": 1, **{"a": 2}} # => {'a': 2} + + +# 세트는... 음, 세트를 저장합니다. +empty_set = set() +# 여러 값으로 세트 초기화 +some_set = {1, 1, 2, 2, 3, 4} # some_set은 이제 {1, 2, 3, 4}입니다. + +# 딕셔너리의 키와 마찬가지로 세트의 요소는 불변이어야 합니다. +invalid_set = {[1], 1} # => TypeError: unhashable type: 'list' 발생 +valid_set = {(1,), 1} + +# 세트에 항목 하나 더 추가 +filled_set = some_set +filled_set.add(5) # filled_set은 이제 {1, 2, 3, 4, 5}입니다. +# 세트에는 중복 요소가 없습니다. +filled_set.add(5) # 이전과 같이 {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} + +# ^로 세트 대칭 차집합 +{1, 2, 3, 4} ^ {2, 3, 5} # => {1, 4, 5} + +# 왼쪽 세트가 오른쪽 세트의 상위 집합인지 확인 +{1, 2} >= {1, 2, 3} # => False + +# 왼쪽 세트가 오른쪽 세트의 하위 집합인지 확인 +{1, 2} <= {1, 2, 3} # => True + +# in으로 세트에 존재 여부 확인 +2 in filled_set # => True +10 in filled_set # => False + +# 한 단계 깊은 복사본 만들기 +filled_set = some_set.copy() # filled_set은 {1, 2, 3, 4, 5}입니다. +filled_set is some_set # => False + + +#################################################### +## 3. 제어 흐름 및 반복 가능 객체 +#################################################### + +# 변수를 만들어 봅시다. +some_var = 5 + +# if 문입니다. Python에서는 들여쓰기가 중요합니다! +# 관례는 탭이 아닌 4개의 공백을 사용하는 것입니다. +# "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.") + +# Match/Case — Python 3.10에 도입됨 +# 여러 패턴에 대해 값을 비교하고 일치하는 케이스 블록을 실행합니다. + +command = "run" + +match command: + case "run": + print("The robot started to run 🏃‍♂️") + case "speak" | "say_hi": # 여러 옵션 (OR 패턴) + print("The robot said hi 🗣️") + case code if command.isdigit(): # 조건부 + print(f"The robot execute code: {code}") + case _: # _는 절대 실패하지 않는 와일드카드입니다 (default/else와 같음) + print("Invalid command ❌") + +# 출력: "the robot started to run 🏃‍♂️" + +""" +For 루프는 리스트를 반복합니다. +출력: + dog is a mammal + cat is a mammal + mouse is a mammal +""" +for animal in ["dog", "cat", "mouse"]: + # format()을 사용하여 서식화된 문자열을 보간할 수 있습니다. + print("{} is a mammal".format(animal)) + +""" +"range(number)"는 0부터 주어진 숫자 +(제외)까지의 숫자 반복 가능 객체를 반환합니다. +출력: + 0 + 1 + 2 + 3 +""" +for i in range(4): + print(i) + +""" +"range(lower, upper)"는 아래 숫자에서 위 숫자까지의 +숫자 반복 가능 객체를 반환합니다. +출력: + 4 + 5 + 6 + 7 +""" +for i in range(4, 8): + print(i) + +""" +"range(lower, upper, step)"는 아래 숫자에서 위 숫자까지 +step만큼 증가하면서 숫자 반복 가능 객체를 반환합니다. +step이 표시되지 않으면 기본값은 1입니다. +출력: + 4 + 6 +""" +for i in range(4, 8, 2): + print(i) + +""" +리스트를 반복하여 각 리스트 항목의 인덱스와 값을 모두 검색합니다. + 0 dog + 1 cat + 2 mouse +""" +animals = ["dog", "cat", "mouse"] +for i, value in enumerate(animals): + print(i, value) + +""" +While 루프는 조건이 더 이상 충족되지 않을 때까지 계속됩니다. +출력: + 0 + 1 + 2 + 3 +""" +x = 0 +while x < 4: + print(x) + x += 1 # x = x + 1의 약어 + +# try/except 블록으로 예외 처리 +try: + # "raise"를 사용하여 오류 발생 + raise IndexError("This is an index error") +except IndexError as e: + pass # 이것은 삼가십시오. 복구를 제공하십시오 (다음 예제). +except (TypeError, NameError): + pass # 여러 예외를 공동으로 처리할 수 있습니다. +else: # try/except 블록의 선택적 절. 모든 + # except 블록을 따라야 합니다. + print("All good!") # try의 코드가 예외를 발생시키지 않을 때만 실행됩니다. +finally: # 모든 상황에서 실행 + print("We can clean up resources here") + +# 리소스 정리를 위해 try/finally 대신 with 문을 사용할 수 있습니다. +with open("myfile.txt") as f: + for line in f: + print(line) + +# 파일에 쓰기 +contents = {"aa": 12, "bb": 21} +with open("myfile1.txt", "w") as file: + file.write(str(contents)) # 파일에 문자열 쓰기 + +import json +with open("myfile2.txt", "w") as file: + file.write(json.dumps(contents)) # 파일에 객체 쓰기 + +# 파일에서 읽기 +with open("myfile1.txt") as file: + contents = file.read() # 파일에서 문자열 읽기 +print(contents) +# 출력: {"aa": 12, "bb": 21} + +with open("myfile2.txt", "r") as file: + contents = json.load(file) # 파일에서 json 객체 읽기 +print(contents) +# 출력: {"aa": 12, "bb": 21} + + +# Python은 반복 가능이라는 기본 추상화를 제공합니다. +# 반복 가능 객체는 시퀀스로 처리할 수 있는 객체입니다. +# range 함수에서 반환된 객체는 반복 가능 객체입니다. + +filled_dict = {"one": 1, "two": 2, "three": 3} +our_iterable = filled_dict.keys() +print(our_iterable) # => dict_keys(['one', 'two', 'three']). 이것은 + # Iterable 인터페이스를 구현하는 객체입니다. + +# 반복할 수 있습니다. +for i in our_iterable: + print(i) # one, two, three 출력 + +# 그러나 인덱스로 요소에 주소를 지정할 수는 없습니다. +our_iterable[1] # TypeError 발생 + +# 반복 가능 객체는 반복자를 만드는 방법을 아는 객체입니다. +our_iterator = iter(our_iterable) + +# 반복자는 반복하면서 상태를 기억할 수 있는 객체입니다. +# "next()"로 다음 객체를 가져옵니다. +next(our_iterator) # => "one" + +# 반복하면서 상태를 유지합니다. +next(our_iterator) # => "two" +next(our_iterator) # => "three" + +# 반복자가 모든 데이터를 반환한 후에는 +# StopIteration 예외를 발생시킵니다. +next(our_iterator) # StopIteration 발생 + +# 반복할 수도 있습니다. 사실 "for"는 암시적으로 이 작업을 수행합니다! +our_iterator = iter(our_iterable) +for i in our_iterator: + print(i) # one, two, three 출력 + +# list() 호출로 반복 가능 객체 또는 반복자의 모든 요소를 가져올 수 있습니다. +list(our_iterable) # => ["one", "two", "three"] 반환 +list(our_iterator) # => [] 반환, 상태가 저장되었기 때문 + + +#################################################### +## 4. 함수 +#################################################### + +# "def"를 사용하여 새 함수 생성 +def add(x, y): + print("x is {} and y is {}".format(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} +""" + +# 함수를 호출할 때 args/kwargs의 반대 작업을 수행할 수 있습니다! +# *를 사용하여 args(튜플)를 확장하고 **를 사용하여 kwargs(딕셔너리)를 확장합니다. +args = (1, 2, 3, 4) +kwargs = {"a": 3, "b": 4} +all_the_args(*args) # 동등: all_the_args(1, 2, 3, 4) +all_the_args(**kwargs) # 동등: all_the_args(a=3, b=4) +all_the_args(*args, **kwargs) # 동등: all_the_args(1, 2, 3, 4, a=3, b=4) + +# 여러 값 반환 (튜플 할당 사용) +def swap(x, y): + return y, x # 괄호 없이 튜플로 여러 값 반환 + # (참고: 괄호는 제외되었지만 포함될 수 있음) + +x = 1 +y = 2 +x, y = swap(x, y) # => x = 2, y = 1 +# (x, y) = swap(x,y) # 다시 괄호 사용은 선택 사항입니다. + +# 전역 범위 +x = 5 + +def set_x(num): + # 지역 범위 시작 + # 지역 변수 x는 전역 변수 x와 다릅니다. + x = num # => 43 + print(x) # => 43 + +def set_global_x(num): + # global은 특정 변수가 전역 범위에 있음을 나타냅니다. + global x + print(x) # => 5 + x = num # 전역 변수 x가 이제 6으로 설정됨 + print(x) # => 6 + +set_x(43) +set_global_x(6) +""" +출력: + 43 + 5 + 6 +""" + + +# Python에는 일급 함수가 있습니다. +def create_adder(x): + def adder(y): + return x + y + return adder + +add_10 = create_adder(10) +add_10(3) # => 13 + +# 중첩된 함수의 클로저: +# nonlocal 키워드를 사용하여 내부 함수에서 선언되어서는 안 되는 중첩된 범위의 변수로 작업할 수 있습니다. +def create_avg(): + total = 0 + count = 0 + def avg(n): + nonlocal total, count + total += n + count += 1 + return total/count + return avg +avg = create_avg() +avg(3) # => 3.0 +avg(5) # (3+5)/2 => 4.0 +avg(7) # (8+7)/3 => 5.0 + +# 익명 함수도 있습니다. +(lambda x: x > 2)(3) # => True +(lambda x, y: x ** 2 + y ** 2)(2, 1) # => 5 + +# 내장 고차 함수가 있습니다. +list(map(add_10, [1, 2, 3])) # => [11, 12, 13] +list(map(max, [1, 2, 3], [4, 2, 1])) # => [4, 2, 3] + +list(filter(lambda x: x > 5, [3, 4, 5, 6, 7])) # => [6, 7] + +# 멋진 맵과 필터를 위해 리스트 내포를 사용할 수 있습니다. +# 리스트 내포는 출력을 리스트로 저장합니다(자체적으로 중첩될 수 있음). +[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] + +# 세트 및 딕셔너리 내포도 구성할 수 있습니다. +{x for x in "abcddeef" if x not in "abc"} # => {'d', 'e', 'f'} +{x: x**2 for x in range(5)} # => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16} + + +#################################################### +## 5. 모듈 +#################################################### + +# 모듈을 가져올 수 있습니다. +import math +print(math.sqrt(16)) # => 4.0 + +# 모듈에서 특정 함수를 가져올 수 있습니다. +from math import ceil, floor +print(ceil(3.7)) # => 4 +print(floor(3.7)) # => 3 + +# 모듈에서 모든 함수를 가져올 수 있습니다. +# 경고: 권장하지 않습니다. +from math import * + +# 모듈 이름을 단축할 수 있습니다. +import math as m +math.sqrt(16) == m.sqrt(16) # => True + +# Python 모듈은 일반 Python 파일일 뿐입니다. +# 직접 작성하고 가져올 수 있습니다. 모듈의 이름은 +# 파일 이름과 동일합니다. + +# 모듈에 정의된 함수와 속성을 +# 찾을 수 있습니다. +import math +dir(math) + +# 현재 스크립트와 동일한 폴더에 math.py라는 +# Python 스크립트가 있는 경우, 내장 Python 모듈 대신 +# math.py 파일이 로드됩니다. +# 이것은 로컬 폴더가 Python의 내장 라이브러리보다 +# 우선순위가 높기 때문에 발생합니다. + + +#################################################### +## 6. 클래스 +#################################################### + +# "class" 문을 사용하여 클래스를 만듭니다. +class Human: + + # 클래스 속성입니다. 이 클래스의 모든 인스턴스에서 공유됩니다. + species = "H. sapiens" + + # 기본 초기화자, 이 클래스가 인스턴스화될 때 호출됩니다. + # 이중 선행 및 후행 밑줄은 Python에서 사용되지만 + # 사용자 제어 네임스페이스에 있는 객체 또는 속성을 나타냅니다. + # __init__, __str__, __repr__ 등과 같은 메서드(또는 객체 또는 속성)는 + # 특수 메서드(때로는 dunder 메서드라고도 함)라고 합니다. + # 이러한 이름을 직접 만들어서는 안 됩니다. + def __init__(self, name): + # 인수를 인스턴스의 이름 속성에 할당 + self.name = name + + # 속성 초기화 + self._age = 0 # 선행 밑줄은 "age" 속성이 + # 내부적으로 사용되도록 의도되었음을 나타냅니다. + # 이것이 강제될 것이라고 의존하지 마십시오. 다른 개발자에게 힌트입니다. + + # 인스턴스 메서드입니다. 모든 메서드는 "self"를 첫 번째 인수로 받습니다. + def say(self, msg): + print("{name}: {message}".format(name=self.name, message=msg)) + + # 다른 인스턴스 메서드 + def sing(self): + return "yo... yo... microphone check... one two... one two..." + + # 클래스 메서드는 모든 인스턴스에서 공유됩니다. + # 호출하는 클래스를 첫 번째 인수로 사용하여 호출됩니다. + @classmethod + def get_species(cls): + return cls.species + + # 정적 메서드는 클래스 또는 인스턴스 참조 없이 호출됩니다. + @staticmethod + def grunt(): + return "*grunt*" + + # 속성은 getter와 같습니다. + # age() 메서드를 동일한 이름의 읽기 전용 속성으로 바꿉니다. + # 하지만 Python에서는 사소한 getter와 setter를 작성할 필요가 없습니다. + @property + def age(self): + return self._age + + # 이렇게 하면 속성을 설정할 수 있습니다. + @age.setter + def age(self, age): + self._age = age + + # 이렇게 하면 속성을 삭제할 수 있습니다. + @age.deleter + def age(self): + del self._age + + +# Python 인터프리터가 소스 파일을 읽을 때 모든 코드를 실행합니다. +# 이 __name__ 검사는 이 코드 블록이 이 모듈이 +# 주 프로그램일 때만 실행되도록 합니다. +if __name__ == "__main__": + # 클래스 인스턴스화 + i = Human(name="Ian") + i.say("hi") # "Ian: hi" + j = Human("Joel") + j.say("hello") # "Joel: hello" + # i와 j는 Human 타입의 인스턴스입니다. 즉, Human 객체입니다. + + # 클래스 메서드 호출 + i.say(i.get_species()) # "Ian: H. sapiens" + # 공유 속성 변경 + Human.species = "H. neanderthalensis" + i.say(i.get_species()) # => "Ian: H. neanderthalensis" + j.say(j.get_species()) # => "Joel: H. neanderthalensis" + + # 정적 메서드 호출 + print(Human.grunt()) # => "*grunt*" + + # 정적 메서드는 인스턴스에서도 호출할 수 있습니다. + print(i.grunt()) # => "*grunt*" + + # 이 인스턴스의 속성 업데이트 + i.age = 42 + # 속성 가져오기 + i.say(i.age) # => "Ian: 42" + j.say(j.age) # => "Joel: 0" + # 속성 삭제 + del i.age + # i.age # => AttributeError 발생 + + +#################################################### +## 6.1 상속 +#################################################### + +# 상속을 통해 부모 클래스에서 메서드와 +# 변수를 상속하는 새로운 자식 클래스를 정의할 수 있습니다. + +# 위에서 정의한 Human 클래스를 기본 또는 부모 클래스로 사용하여 +# 자식 클래스인 Superhero를 정의할 수 있습니다. Superhero는 "species", +# "name", "age"와 같은 변수와 "sing", "grunt"와 같은 메서드를 +# Human 클래스에서 상속하지만, 자신만의 고유한 속성을 가질 수도 있습니다. + +# 파일별 모듈화를 활용하려면 위 클래스를 +# human.py와 같은 자체 파일에 배치할 수 있습니다. + +# 다른 파일에서 함수를 가져오려면 다음 형식을 사용하십시오. +# from "파일이름-확장자없음" import "함수-또는-클래스" + +from human import Human + + +# 부모 클래스를 클래스 정의의 매개변수로 지정 +class Superhero(Human): + + # 자식 클래스가 부모의 모든 정의를 수정 없이 + # 상속해야 하는 경우 "pass" 키워드를 사용할 수 있습니다(다른 것은 없음). + # 하지만 이 경우 고유한 자식 클래스를 허용하기 위해 주석 처리되었습니다. + # pass + + # 자식 클래스는 부모의 속성을 재정의할 수 있습니다. + species = "Superhuman" + + # 자식은 부모 클래스의 생성자를 자동으로 상속하며 + # 인수도 포함하지만, 추가 인수를 정의하거나 정의하고 + # 클래스 생성자와 같은 메서드를 재정의할 수도 있습니다. + # 이 생성자는 "Human" 클래스에서 "name" 인수를 상속하고 + # "superpower" 및 "movie" 인수를 추가합니다. + def __init__(self, name, movie=False, + superpowers=["super strength", "bulletproofing"]): + + # 추가 클래스 속성 추가: + self.fictional = True + self.movie = movie + # 기본값이 공유되므로 가변 기본값에 유의하십시오. + self.superpowers = superpowers + + # "super" 함수를 사용하면 자식에 의해 재정의된 + # 부모 클래스의 메서드에 액세스할 수 있습니다. 이 경우 __init__ 메서드입니다. + # 이것은 부모 클래스 생성자를 호출합니다. + super().__init__(name) + + # sing 메서드 재정의 + def sing(self): + return "Dun, dun, DUN!" + + # 추가 인스턴스 메서드 추가 + def boast(self): + for power in self.superpowers: + print("I wield the power of {pow}!".format(pow=power)) + + +if __name__ == "__main__": + sup = Superhero(name="Tick") + + # 인스턴스 타입 확인 + if isinstance(sup, Human): + print("I am human") + if type(sup) is Superhero: + print("I am a superhero") + + # getattr() 및 super()에서 사용하는 "메서드 확인 순서" 가져오기 + # (속성 또는 메서드를 검색하는 클래스 순서) + # 이 속성은 동적이며 업데이트할 수 있습니다. + print(Superhero.__mro__) # => (, + # => , ) + + # 부모 메서드를 호출하지만 자체 클래스 속성을 사용 + print(sup.get_species()) # => Superhuman + + # 재정의된 메서드 호출 + print(sup.sing()) # => Dun, dun, DUN! + + # Human에서 메서드 호출 + sup.say("Spoon") # => Tick: Spoon + + # Superhero에만 존재하는 메서드 호출 + sup.boast() # => I wield the power of super strength! + # => I wield the power of bulletproofing! + + # 상속된 클래스 속성 + sup.age = 31 + print(sup.age) # => 31 + + # Superhero 내에만 존재하는 속성 + print("Am I Oscar eligible? " + str(sup.movie)) + +#################################################### +## 6.2 다중 상속 +#################################################### + + +# 다른 클래스 정의 +# bat.py +class Bat: + + species = "Baty" + + def __init__(self, can_fly=True): + self.fly = can_fly + + # 이 클래스에도 say 메서드가 있습니다. + def say(self, msg): + msg = "... ... ..." + return msg + + # 그리고 자신만의 메서드도 있습니다. + def sonar(self): + return "))) ... (((" + + +if __name__ == "__main__": + b = Bat() + print(b.say("hello")) + print(b.fly) + + +# 그리고 Superhero와 Bat에서 상속하는 또 다른 클래스 정의 +# superhero.py +from superhero import Superhero +from bat import Bat + +# Batman을 Superhero와 Bat 모두에서 상속하는 자식으로 정의 +class Batman(Superhero, Bat): + + def __init__(self, *args, **kwargs): + # 일반적으로 속성을 상속하려면 super를 호출해야 합니다. + # super(Batman, self).__init__(*args, **kwargs) + # 하지만 여기서는 다중 상속을 다루고 있으며, super()는 + # MRO 목록의 다음 기본 클래스에서만 작동합니다. + # 따라서 대신 모든 조상에 대해 명시적으로 __init__을 호출합니다. + # *args와 **kwargs를 사용하면 각 부모가 "양파 껍질을 벗기는" + # 방식으로 인수를 깔끔하게 전달할 수 있습니다. + Superhero.__init__(self, "anonymous", movie=True, + superpowers=["Wealthy"], *args, **kwargs) + Bat.__init__(self, *args, can_fly=False, **kwargs) + # name 속성 값 재정의 + self.name = "Sad Affleck" + + def sing(self): + return "nan nan nan nan nan batman!" + + +if __name__ == "__main__": + sup = Batman() + + # 메서드 확인 순서 + print(Batman.__mro__) # => (, + # => , + # => , + # => , ) + + # 부모 메서드를 호출하지만 자체 클래스 속성을 사용 + print(sup.get_species()) # => Superhuman + + # 재정의된 메서드 호출 + print(sup.sing()) # => nan nan nan nan nan batman! + + # 상속 순서가 중요하므로 Human에서 메서드 호출 + sup.say("I agree") # => Sad Affleck: I agree + + # 두 번째 조상에만 존재하는 메서드 호출 + print(sup.sonar()) # => ))) ... ((( + + # 상속된 클래스 속성 + sup.age = 100 + print(sup.age) # => 100 + + # 기본값이 재정의된 두 번째 조상에서 상속된 속성 + print("Can I fly? " + str(sup.fly)) # => Can I fly? False + + +#################################################### +## 7. 고급 +#################################################### + +# 제너레이터는 게으른 코드를 만드는 데 도움이 됩니다. +def double_numbers(iterable): + for i in iterable: + yield i + i + +# 제너레이터는 반복 가능한 객체에서 다음 값을 처리하는 데 필요한 +# 데이터만 로드하기 때문에 메모리 효율적입니다. +# 이를 통해 금지적으로 큰 값 범위에 대한 작업을 수행할 수 있습니다. +# 참고: `range`는 Python 3에서 `xrange`를 대체합니다. +for i in double_numbers(range(1, 900000000)): # `range`는 제너레이터입니다. + print(i) + if i >= 30: + break + +# 리스트 내포를 만들 수 있는 것처럼 제너레이터 +# 내포도 만들 수 있습니다. +values = (-x for x in [1,2,3,4,5]) +for x in values: + print(x) # 콘솔/터미널에 -1 -2 -3 -4 -5 출력 + +# 제너레이터 내포를 리스트로 직접 캐스팅할 수도 있습니다. +values = (-x for x in [1,2,3,4,5]) +gen_to_list = list(values) +print(gen_to_list) # => [-1, -2, -3, -4, -5] + + +# 데코레이터는 구문 설탕의 한 형태입니다. +# 투박한 구문을 달성하면서 코드를 더 쉽게 읽을 수 있도록 합니다. + +# 래퍼는 데코레이터의 한 유형입니다. +# 기존 함수를 수정할 필요 없이 로깅을 추가하는 데 정말 유용합니다. + +def log_function(func): + def wrapper(*args, **kwargs): + print("Entering function", func.__name__) + result = func(*args, **kwargs) + print("Exiting function", func.__name__) + return result + return wrapper + +@log_function # 동등: +def my_function(x,y): # def my_function(x,y): + """Adds two numbers together.""" + return x+y # return x+y + # my_function = log_function(my_function) +# 데코레이터 @log_function은 my_function에 대한 함수 정의를 읽기 시작할 때 +# 이 함수가 log_function으로 래핑될 것임을 알려줍니다. +# 함수 정의가 길면 정의 끝에 있는 데코레이션되지 않은 +# 할당을 구문 분석하기 어려울 수 있습니다. + +my_function(1,2) # => "Entering function my_function" + # => "3" + # => "Exiting function my_function" + +# 하지만 문제가 있습니다. +# my_function에 대한 정보를 얻으려고 하면 어떻게 될까요? + +print(my_function.__name__) # => 'wrapper' +print(my_function.__doc__) # => None (래퍼 함수에는 docstring이 없음) + +# 데코레이터가 my_function = log_function(my_function)과 동일하기 때문에 +# my_function에 대한 정보를 래퍼 정보로 대체했습니다. + +# functools를 사용하여 이 문제를 해결하십시오. + +from functools import wraps + +def log_function(func): + @wraps(func) # 이것은 docstring, 함수 이름, 인수 목록 등이 모두 + # 래핑된 함수에 복사되도록 보장합니다 - 래퍼 정보로 대체되는 대신 + def wrapper(*args, **kwargs): + print("Entering function", func.__name__) + result = func(*args, **kwargs) + print("Exiting function", func.__name__) + return result + return wrapper + +@log_function +def my_function(x,y): + """Adds two numbers together.""" + return x+y + +my_function(1,2) # => "Entering function my_function" + # => "3" + # => "Exiting function my_function" + +print(my_function.__name__) # => 'my_function' +print(my_function.__doc__) # => 'Adds two numbers together.' +``` + +### 무료 온라인 + +* [파이썬으로 지루한 작업 자동화하기](https://automatetheboringstuff.com) +* [공식 문서](https://docs.python.org/3/) +* [파이썬을 위한 히치하이커 안내서](https://docs.python-guide.org/) +* [파이썬 코스](https://www.python-course.eu) +* [파이썬 첫걸음](https://realpython.com/learn/python-first-steps/) +* [엄선된 멋진 파이썬 프레임워크, 라이브러리 및 소프트웨어 목록](https://github.com/vinta/awesome-python) +* [파이썬 공식 스타일 가이드](https://peps.python.org/pep-0008/) +* [파이썬 3 컴퓨터 과학 서클](https://cscircles.cemc.uwaterloo.ca/) +* [파이썬 3으로 뛰어들기](https://www.diveintopython3.net/) +* [중급자를 위한 파이썬 튜토리얼](https://pythonbasics.org/) +* [파이썬으로 데스크톱 앱 만들기](https://pythonpyqt.com/) diff --git a/ko/pythonstatcomp.md b/ko/pythonstatcomp.md new file mode 100644 index 0000000000..d6be245536 --- /dev/null +++ b/ko/pythonstatcomp.md @@ -0,0 +1,239 @@ +--- +category: framework +name: Statistical computing with Python +contributors: + - ["e99n09", "https://github.com/e99n09"] +filename: pythonstatcomp.py +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +이것은 파이썬을 사용하여 몇 가지 일반적인 통계 프로그래밍 작업을 수행하는 방법에 대한 튜토리얼입니다. 기본적으로 파이썬에 익숙하고 R, Stata, SAS, SPSS 또는 MATLAB과 같은 언어로 통계 프로그래밍 경험이 있는 사람들을 대상으로 합니다. + +```python +# 0. 설정하기 ==== + +""" 시작하려면 다음을 pip 설치하십시오: jupyter, numpy, scipy, pandas, + matplotlib, seaborn, requests. + 인라인 플롯과 쉬운 문서 조회를 위해 이 튜토리얼을 + Jupyter 노트북에서 수행해야 합니다. 셸 명령어는 + `jupyter notebook`이며, New -> Python을 클릭하십시오. +""" + +# 1. 데이터 수집 ==== + +""" 사람들이 R보다 파이썬을 선택하는 한 가지 이유는 웹과 + 많이 상호 작용하려는 의도 때문입니다. 직접 페이지를 스크래핑하거나 + API를 통해 데이터를 요청하는 등. R에서도 이러한 작업을 할 수 있지만, + 이미 파이썬을 사용하는 프로젝트의 맥락에서는 한 가지 언어를 + 고수하는 것이 이점이 있습니다. +""" + +import requests # HTTP 요청용 (웹 스크래핑, API) +import os + +# 웹 스크래핑 +r = requests.get("https://github.com/adambard/learnxinyminutes-docs") +r.status_code # 200이면 요청이 성공한 것입니다. +r.text # 원시 페이지 소스 +print(r.text) # 예쁘게 서식 지정됨 +# 페이지 소스를 파일에 저장: +os.getcwd() # 작업 디렉토리 확인 +with open("learnxinyminutes.html", "wb") as f: + f.write(r.text.encode("UTF-8")) + +# csv 다운로드 +fp = "https://raw.githubusercontent.com/adambard/learnxinyminutes-docs/master/" +fn = "pets.csv" +r = requests.get(fp + fn) +print(r.text) +with open(fn, "wb") as f: + f.write(r.text.encode("UTF-8")) + +""" requests 모듈에 대한 자세한 내용, API 포함, + http://docs.python-requests.org/en/latest/user/quickstart/ 참조 +""" + +# 2. CSV 파일 읽기 ==== + +""" Wes McKinney의 pandas 패키지는 파이썬에서 'DataFrame' 객체를 제공합니다. + R을 사용해 본 적이 있다면 이미 "data.frame"의 개념에 익숙할 것입니다. +""" + +import pandas as pd +import numpy as np +import scipy as sp +pets = pd.read_csv(fn) +pets +# name age weight species +# 0 fluffy 3 14 cat +# 1 vesuvius 6 23 fish +# 2 rex 5 34 dog + +""" R 사용자는 C의 영향을 받은 대부분의 프로그래밍 언어와 마찬가지로 파이썬은 + 0부터 인덱싱을 시작한다는 점에 유의해야 합니다. R은 Fortran의 영향으로 1부터 + 인덱싱을 시작합니다. +""" + +# 열을 출력하는 두 가지 다른 방법 +pets.age +pets["age"] + +pets.head(2) # 처음 2개 행 출력 +pets.tail(1) # 마지막 행 출력 + +pets.name[1] # 'vesuvius' +pets.species[0] # 'cat' +pets["weight"][2] # 34 + +# R에서는 이렇게 하면 3개의 행을 얻을 것으로 예상하지만, 여기서는 2개를 얻습니다. +pets.age[0:2] +# 0 3 +# 1 6 + +sum(pets.age) * 2 # 28 +max(pets.weight) - min(pets.weight) # 20 + +""" 심각한 선형 대수 및 숫자 계산을 수행하는 경우 + DataFrame이 아닌 배열을 원할 수 있습니다. DataFrame은 + 다른 유형의 열을 결합하는 데 이상적입니다. +""" + +# 3. 차트 ==== + +import matplotlib as mpl +import matplotlib.pyplot as plt +%matplotlib inline + +# 파이썬에서 데이터 시각화를 하려면 matplotlib를 사용하십시오. + +plt.hist(pets.age); + +plt.boxplot(pets.weight); + +plt.scatter(pets.age, pets.weight) +plt.xlabel("age") +plt.ylabel("weight"); + +# seaborn은 matplotlib 위에 있으며 플롯을 더 예쁘게 만듭니다. + +import seaborn as sns + +plt.scatter(pets.age, pets.weight) +plt.xlabel("age") +plt.ylabel("weight"); + +# seaborn 전용 플로팅 함수도 있습니다. +# 이 막대 그래프에서 seaborn이 x축에 자동으로 레이블을 지정하는 방법을 확인하십시오. +sns.barplot(pets["age"]) + +# R 베테랑은 여전히 ggplot을 사용할 수 있습니다. +from ggplot import * +ggplot(aes(x="age",y="weight"), data=pets) + geom_point() + labs(title="pets") +# 출처: https://pypi.python.org/pypi/ggplot + +# d3.js 포트도 있습니다: https://github.com/mikedewar/d3py + +# 4. 간단한 데이터 정리 및 탐색적 분석 ==== + +""" 다음은 일부 탐색적 플롯 생성 및 선형 회귀 실행으로 + 이어지는 기본 데이터 정리 워크플로를 보여주는 더 복잡한 예입니다. + 데이터 세트는 위키백과에서 직접 필사되었습니다. 여기에는 + 모든 신성 로마 황제와 그들의 삶의 중요한 이정표(출생, 사망, + 대관식 등)가 포함되어 있습니다. + 분석의 목표는 황제 출생 연도와 황제 수명 사이에 + 관계가 있는지 탐색하는 것입니다. + 데이터 출처: https://en.wikipedia.org/wiki/Holy_Roman_Emperor +""" + +# 신성 로마 황제에 대한 일부 데이터 로드 +url = "https://raw.githubusercontent.com/adambard/learnxinyminutes-docs/master/hre.csv" +r = requests.get(url) +fp = "hre.csv" +with open(fp, "wb") as f: + f.write(r.text.encode("UTF-8")) + +hre = pd.read_csv(fp) + +hre.head() +""" + Ix Dynasty Name Birth Death +0 NaN Carolingian Charles I 2 April 742 28 January 814 +1 NaN Carolingian Louis I 778 20 June 840 +2 NaN Carolingian Lothair I 795 29 September 855 +3 NaN Carolingian Louis II 825 12 August 875 +4 NaN Carolingian Charles II 13 June 823 6 October 877 + + Coronation 1 Coronation 2 Ceased to be Emperor +0 25 December 800 NaN 28 January 814 +1 11 September 813 5 October 816 20 June 840 +2 5 April 823 NaN 29 September 855 +3 Easter 850 18 May 872 12 August 875 +4 29 December 875 NaN 6 October 877 +""" + +# 출생 및 사망 열 정리 + +import re # 정규식 모듈 + +rx = re.compile(r'\d+$') # 후행 숫자 일치 + +""" 이 함수는 정규식을 입력 열(여기서는 출생, 사망)에 적용하고, + 결과 목록을 평탄화하고, Series 객체로 변환하고, + 마지막으로 Series 객체의 유형을 문자열에서 정수로 변환합니다. + 코드의 다른 부분이 무엇을 하는지에 대한 자세한 내용은 다음을 참조하십시오. + - https://docs.python.org/2/howto/regex.html + - http://stackoverflow.com/questions/11860476/how-to-unlist-a-python-list + - http://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.html +""" + +from functools import reduce + +def extractYear(v): + return(pd.Series(reduce(lambda x, y: x + y, map(rx.findall, v), [])).astype(int)) + +hre["BirthY"] = extractYear(hre.Birth) +hre["DeathY"] = extractYear(hre.Death) + +# 예상 수명을 알려주는 열 만들기 +hre["EstAge"] = hre.DeathY.astype(int) - hre.BirthY.astype(int) + +# 간단한 산점도, 추세선 없음, 색상은 왕조를 나타냄 +sns.lmplot("BirthY", "EstAge", data=hre, hue="Dynasty", fit_reg=False) + +# scipy를 사용하여 선형 회귀 실행 +from scipy import stats +(slope, intercept, rval, pval, stderr) = stats.linregress(hre.BirthY, hre.EstAge) +# 코드 출처: http://wiki.scipy.org/Cookbook/LinearRegression + +# 기울기 확인 +slope # 0.0057672618839073328 + +# R^2 값 확인: +rval**2 # 0.020363950027333586 + +# p-값 확인 +pval # 0.34971812581498452 + +# seaborn을 사용하여 산점도를 만들고 선형 회귀 추세선 플롯 +sns.lmplot("BirthY", "EstAge", data=hre) + +""" seaborn에 대한 자세한 내용은 다음을 참조하십시오. + - http://web.stanford.edu/~mwaskom/software/seaborn/ + - https://github.com/mwaskom/seaborn + SciPy에 대한 자세한 내용은 다음을 참조하십시오. + - http://wiki.scipy.org/SciPy + - http://wiki.scipy.org/Cookbook/ + R을 사용한 신성 로마 황제 분석 버전을 보려면 다음을 참조하십시오. + - http://github.com/e99n09/R-notes/blob/master/holy_roman_emperors_dates.R +""" +``` + +더 배우고 싶다면 Wes McKinney의 _Python for Data Analysis_를 구하십시오. 이 튜토리얼을 작성할 때 참고 자료로 사용한 훌륭한 자료입니다. + +Cam Davidson-Pilon의 [해커를 위한 확률론적 프로그래밍 및 베이지안 방법](http://camdavidsonpilon.github.io/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/)과 같이 관심 분야에 특정한 주제에 대한 대화형 IPython 튜토리얼도 많이 찾을 수 있습니다. + +연구할 추가 모듈: + + - 텍스트 분석 및 자연어 처리: [nltk](http://www.nltk.org) + - 소셜 네트워크 분석: [igraph](http://igraph.org/python/) diff --git a/ko/qml.md b/ko/qml.md new file mode 100644 index 0000000000..acc97aa2c0 --- /dev/null +++ b/ko/qml.md @@ -0,0 +1,366 @@ +--- +name: QML +contributors: + - ["Furkan Uzumcu", "https://zmc.space/"] +filename: learnqml.qml +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```qml +// 이것은 완전히 유효한 QML 파일이며, 내용을 *.qml 파일에 복사하면 `qmlscene`을 사용하여 실행할 수 있습니다. +// 주석은 이중 슬래시로 시작합니다. +/* 또는 + 여러 줄 + 주석을 + 사용할 수 있습니다. + */ + +// import 문 구문은 다음과 같습니다. +// import ${MODULE_NAME} [${VERSION_NUMBER}] [as ${QUALIFIER}] +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 as QQC +import QtQuick.Layouts 1.15 +import Qt.labs.platform 1.1 + +// 각 QML 문서는 최상위 유형을 하나만 포함할 수 있습니다. +Window { + // 각 객체에는 선언된 객체를 참조하는 데 사용할 수 있는 특별하고 선택적인 `id` 속성이 있습니다. + // `id`는 동일한 문서 내에서 고유해야 합니다. + id: root + width: 400 + height: 600 + title: "Y분 안에 QML 배우기" + + Item { + // 선언할 수 있는 모든 객체는 QObject에서 상속되며, + // `objectName`이라는 속성을 하나 이상 포함합니다. 다른 모든 속성은 + // `QObject` 유형을 확장하여 추가됩니다. 이것은 `Item` 유형이며 + // 추가적인 `width` 및 `height` 속성 등을 포함합니다. + objectName: "My Item" + // 동일한 문서의 `id`는 동일한 파일의 어디에서나 사용할 수 있습니다. + // 다른 파일에서는 `id`에 액세스할 수 없습니다. + width: root.width + } + + // 시그널은 특정 이벤트가 발생했음을 알리는 데 사용됩니다. + // 일부 유형에는 내장 시그널이 있습니다. + Timer { + id: timer + interval: 500 + onTriggered: { + console.log("타이머가 트리거되었습니다!") + } + } + + QtObject { + id: objSignals + // 자신만의 시그널을 선언할 수도 있습니다. + signal clicked() + // 시그널에는 인수가 있을 수도 있습니다. + signal mousePositionChanged(int x, int y) + // 시그널 방출에 반응하는 방법은 시그널이 속한 + // 즉각적인 객체에 시그널 핸들러를 추가하는 것입니다. + onClicked: () => { + // 여기서 작업을 수행합니다. + console.log("objSignals.clicked() 시그널이 방출되었습니다.") + } + // 시그널 핸들러는 인수를 명시적으로 선언해야 합니다. + onMousePositionChanged: (x, y) => { + // 여기서 작업을 수행합니다. + console.log("objSignals.mousePositionChanged() 시그널이 방출되었습니다. x=", x, "y=", y) + } + } + + // 다른 객체에 대한 시그널 핸들러를 선언하려면 + // `Connections`를 사용할 수 있습니다. + Connections { + target: objSignals + + // 그런 다음 시그널 핸들러와 동일한 이름의 함수를 + // 선언할 수 있습니다. + function onClicked() { + console.log("objSignals.clicked() 시그널이 Connections에서 처리되었습니다.") + } + } + + Item { + visible: false + + // 객체는 자식 객체를 가질 수 있습니다. 다음과 같이 + // 유형을 선언하여 자식 객체를 추가할 수 있습니다. + Rectangle { + width: 16 + height: 16 + color: "red" + } + } + + Item { + id: objProperties + // 자신만의 속성을 선언할 수도 있습니다. + // 선언 구문은 다음과 같습니다. + // [default] [required] [readonly] property ${TYPE} ${NAME} + property color nextColor + // 읽기 전용 속성은 선언 시 초기화해야 합니다. + readonly property color defaultColor: "red" + // 필수 속성은 재사용 가능한 유형이 + // 사용되는 곳에서 초기화해야 합니다. + required property color initialColor + + // 참고: 초기 할당은 동일한 파일에서 수행할 수 있지만, + // 일반적인 사용 사례는 아닙니다. + initialColor: "green" + + // 속성은 유형에 안전하며 속성에는 속성 유형과 + // 일치하는 값만 할당할 수 있습니다. + // property int volume: "four" // 오류! + + Item { + // 다른 속성에 대한 참조를 보유하는 별칭 속성을 + // 만들 수 있습니다. + + property alias parentNextColor: objProperties.nextColor + + // 별칭 속성에 대한 할당은 참조하는 속성을 + // 변경합니다. + parentNextColor: "blue" // objProperties.nextColor 변경 + // `parentNextColor`가 `nextColor`의 별칭이므로 `nextColor`에 + // 대한 모든 변경 사항은 `parentNextColor`에도 반영됩니다. + } + } + + Item { + // 속성 할당 값은 정적이거나 바인딩 표현식일 수 있습니다. + // 정적 값 + property int radius: 32 + // 바인딩 표현식은 속성과 다른 속성 간의 관계를 설명합니다. + // `radius` 값이 변경되면 여기의 표현식이 + // 다시 평가됩니다. + property int diameter: radius * 2 + + onDiameterChanged: { + console.log("onDiameterChanged:", diameter) + } + } + + ListView { + // 연결된 속성 및 시그널 핸들러는 기존 객체를 확장하고 + // 즉시 사용할 수 없는 추가 정보를 제공하는 방법을 제공합니다. + width: 100 + height: 30 + model: 3 + delegate: Rectangle { + // ListView는 자식에 대해 더 많은 정보에 액세스하는 데 + // 사용할 수 있는 연결된 속성을 제공합니다. + color: ListView.isCurrentItem ? "green" : "red" + } + // 연결된 유형에는 시그널 핸들러가 있을 수도 있습니다. + // `Component`는 QML에서 사용할 수 있는 모든 유형에 연결됩니다. + Component.onCompleted: { + console.log("이 시그널 핸들러는 객체가 생성된 후 호출됩니다.") + } + } + + Rectangle { + // 이 사각형은 ListView에서 생성되지 않았으므로 연결된 + // 유형을 사용할 수 없습니다. + color: ListView.isCurrentItem ? "green" : "red" + } + + QtObject { + id: calculator + + // 객체는 메서드를 선언할 수도 있습니다. 함수 선언은 + // 인수를 주석으로 달거나 인수가 없을 수도 있습니다. + function add(a: int, b: int): int { + // 줄 끝의 세미콜론은 선택 사항입니다. + return a + b + } + + function multiply(a: real, b: real): real { + return a * b; + } + } + + MouseArea { + anchors.fill: parent + onClicked: (mouse) => { + console.log("2 + 2 =", calculator.add(2, 2)) + } + } + + Item { + width: 100 + // 메서드는 바인딩 표현식으로도 사용할 수 있습니다. `width`가 + // 변경되면 바인딩 표현식이 평가되고 `multiply`가 호출됩니다. + height: calculator.multiply(width, 0.5) + opacity: calculateOpacity() + + function calculateOpacity() { + // 함수 선언에 다른 속성에 대한 참조가 포함된 경우 + // 해당 속성에 대한 변경 사항도 바인딩 + // 평가를 트리거합니다. + return height < 50 ? 0.5 : 1 + } + } + + // 대문자 이름으로 시작하는 각 QML 파일은 재사용 가능한 + // 구성 요소를 선언합니다(예: "RedRectangle.qml"). + // 또한 재사용 가능한 구성 요소를 인라인으로 선언할 수 있습니다. + component RedRectangle: Rectangle { + color: "red" + } + + // 이 인라인 구성 요소는 동일한 파일에서 사용하거나 다른 + // 파일에서 유형 이름 앞에 속한 파일 이름을 접두사로 붙여 사용할 수 있습니다. + // + // ${FILE_NAME}.RedRectangle { } + // 또는 + RedRectangle { + } + + // QML은 열거형 선언도 지원합니다. + component MyText: Text { + enum TextType { + Normal, + Heading + } + + // 열거형 유형은 정수 속성에 할당됩니다. + property int textType: MyText.TextType.Normal + + font.bold: textType == MyText.TextType.Heading + font.pixelSize: textType == MyText.TextType.Heading ? 24 : 12 + } + + // ----- 대화형 영역 + + QQC.ScrollView { + anchors.fill: parent + contentWidth: container.implicitWidth + contentHeight: container.implicitHeight + + Column { + id: container + spacing: 6 + + Row { + spacing: 2 + + QQC.Label { + width: 200 + anchors.verticalCenter: parent.verticalCenter + text: "타이머를 시작하려면 클릭하십시오.\n로그를 확인하십시오!" + wrapMode: QQC.Label.WordWrap + } + + QQC.Button { + text: timer.running ? "타이머 실행 중" : "타이머 시작" + onClicked: { + timer.start() + } + } + } + + Row { + spacing: 2 + + QQC.Label { + width: 200 + anchors.verticalCenter: parent.verticalCenter + text: "objSignals.clicked() 시그널을 방출하려면 클릭하십시오." + wrapMode: QQC.Label.WordWrap + } + + QQC.Button { + property int emissionCount: 0 + + text: emissionCount + "번 방출됨." + onClicked: { + objSignals.clicked() + emissionCount++ + } + } + } + + Row { + spacing: 2 + + QQC.Label { + width: 200 + anchors.verticalCenter: parent.verticalCenter + text: "objSignals.mousePositionChanged() 시그널을 방출하려면 클릭하십시오." + wrapMode: QQC.Label.WordWrap + } + + QQC.Button { + property int emissionCount: 0 + + text: emissionCount + "번 방출됨." + onClicked: { + objSignals.mousePositionChanged(32, 32) + emissionCount++ + } + } + } + + Rectangle { + width: 200 + height: 80 + color: objProperties.nextColor + + QQC.Label { + width: 200 + anchors.verticalCenter: parent.verticalCenter + text: "nextColor 속성을 변경하려면 클릭하십시오." + wrapMode: QQC.Label.WordWrap + } + + TapHandler { + onTapped: { + colorDialog.open() + } + } + + ColorDialog { + id: colorDialog + currentColor: objProperties.initialColor + onColorChanged: { + objProperties.nextColor = color + + } + } + } + + Row { + spacing: 2 + + Rectangle { + width: 200 + height: 80 + color: "red" + radius: radiusSlider.value + + QQC.Label { + width: parent.width + anchors.centerIn: parent + text: "반지름을 변경하려면 슬라이더를 사용하십시오." + wrapMode: QQC.Label.WordWrap + horizontalAlignment: Qt.AlignHCenter + } + } + + QQC.Slider { + id: radiusSlider + width: 100 + anchors.verticalCenter: parent.verticalCenter + from: 0 + to: 80 + } + } + } + } +} +``` diff --git a/ko/qsharp.md b/ko/qsharp.md new file mode 100644 index 0000000000..64d76bb91e --- /dev/null +++ b/ko/qsharp.md @@ -0,0 +1,211 @@ +--- +name: Q# +contributors: + - ["Vincent van Wingerden", "https://github.com/vivanwin"] + - ["Mariia Mykhailova", "https://github.com/tcNickolas"] + - ["Andrew Ryan Davis", "https://github.com/AndrewDavis1191"] + - ["Alex Hansen", "https://github.com/sezna"] +filename: LearnQSharp.qs +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Q#은 개발자가 양자 알고리즘을 작성할 수 있도록 하는 고급 도메인 특정 언어입니다. Q# 프로그램은 클래식 컴퓨터에서 실행되는 양자 시뮬레이터와 (향후) 양자 컴퓨터에서 실행할 수 있습니다. + +```c# +// 한 줄 주석은 //로 시작합니다 + + +///////////////////////////////////// +// 1. 양자 데이터 유형 및 연산자 + +// 양자 프로그램의 가장 중요한 부분은 큐비트입니다. +// Q#에서 Qubit 유형은 사용할 수 있는 큐비트를 나타냅니다. +// 이것은 변수 qs로 두 개의 새 큐비트 배열을 할당합니다. +operation QuantumDataTypes() : Unit { + use qs = Qubit[2]; + + // 큐비트에는 직접 읽거나 수정할 수 없는 내부 상태가 있습니다. + // 클래식 시뮬레이터에서 실행하는 경우 양자 프로그램의 + // 현재 상태를 검사할 수 있습니다. + // 실제 양자 하드웨어에서는 작동하지 않습니다! + Std.Diagnostics.DumpMachine(); + + // 큐비트의 상태를 변경하려면 + // 큐비트에 양자 게이트를 적용해야 합니다. + H(qs[0]); // 이것은 첫 번째 큐비트의 상태를 변경합니다 + // |0⟩ (할당된 큐비트의 초기 상태)에서 + // (|0⟩ + |1⟩) / sqrt(2)로. + // qs[1] = |1⟩; - 이것은 작동하지 않으며, 게이트를 사용하여 큐비트를 조작해야 합니다. + + // 여러 큐비트에 다중 큐비트 게이트를 적용할 수 있습니다. + CNOT(qs[0], qs[1]); + + // 게이트의 제어된 버전을 적용할 수도 있습니다: + // 모든 제어 큐비트가 |1⟩ 상태일 때 적용되는 게이트입니다. + // 첫 번째 인수는 제어 큐비트 배열이고, + // 두 번째 인수는 대상 큐비트입니다. + Controlled Y([qs[0]], qs[1]); + + // 역제어 게이트를 적용하려면 + // (모든 제어 큐비트가 |0⟩ 상태일 때 적용되는 게이트), + // 라이브러리 함수를 사용할 수 있습니다. + ApplyControlledOnInt(0, X, [qs[0]], qs[1]); + + // 양자 시스템에서 정보를 읽으려면 측정을 사용합니다. + // 측정은 Result 데이터 유형의 값(Zero 또는 One)을 반환합니다. + // 측정 결과를 클래식 값으로 인쇄할 수 있습니다. + Message($"Measured {M(qs[0])}, {M(qs[1])}"); +} + + +///////////////////////////////////// +// 2. 클래식 데이터 유형 및 연산자 + +function ClassicalDataTypes() : Unit { + // Q#의 숫자는 Int, BigInt 또는 Double에 저장할 수 있습니다. + let i = 1; // 이것은 1과 같은 Int 변수 i를 정의합니다 + let bi = 1L; // 이것은 1과 같은 BigInt 변수 bi를 정의합니다 + let d = 1.0; // 이것은 1과 같은 Double 변수 d를 정의합니다 + + // 유형이 동일한 한 산술은 예상대로 수행됩니다 + let n = 2 * 10; // = 20 + // Q#에는 암시적 유형 캐스트가 없으므로 + // 다른 유형의 값에 대해 산술을 수행하려면 + // 명시적으로 유형을 캐스트해야 합니다 + let nd = Std.Convert.IntAsDouble(2) * 1.0; // = 20.0 + + // 부울 유형은 Bool이라고 합니다 + let trueBool = true; + let falseBool = false; + + // 논리 연산자는 예상대로 작동합니다 + let andBool = true and false; + let orBool = true or false; + let notBool = not false; + + // 문자열 + let str = "Hello World!"; + + // 등호는 == 입니다 + let x = 10 == 15; // 거짓입니다 + + // 범위는 정수 시퀀스이며 다음과 같이 정의할 수 있습니다: start..step..stop + let xi = 1..2..7; // 시퀀스 1,3,5,7을 제공합니다 + + // 변수에 새 값 할당: + // 기본적으로 모든 Q# 변수는 불변입니다. + // 변수가 let을 사용하여 정의된 경우 값을 다시 할당할 수 없습니다. + + // 변수를 변경 가능하게 만들려면 다음과 같이 선언해야 합니다. + // 그리고 set 단어를 사용하여 값을 업데이트합니다 + mutable xii = true; + set xii = false; + + // 다음과 같이 모든 데이터 유형에 대한 배열을 만들 수 있습니다 + let xiii = [0.0, size = 10]; + + // 배열에서 요소 가져오기 + let xiv = xiii[8]; + + // 배열 요소에 새 값 할당 + mutable xv = [0.0, size = 10]; + set xv w/= 5 <- 1.0; +} + + +///////////////////////////////////// +// 3. 제어 흐름 + +operation ControlFlow() : Unit { + let a = 1; + // If 표현식은 true 분기, elif 및 else를 지원합니다. + if (a == 1) { + // ... + } elif (a == 2) { + // ... + } else { + // ... + } + use qubits = Qubit[2]; + + // For 루프를 사용하여 배열을 반복할 수 있습니다 + for qubit in qubits { + X(qubit); + } + + // 일반 for 루프를 사용하여 숫자 범위를 반복할 수 있습니다 + for index in 0..Length(qubits) - 1 { + X(qubits[index]); + } + + // While 루프는 클래식 컨텍스트에서만 사용하도록 제한됩니다 + mutable index = 0; + while (index < 10) { + set index += 1; + } + + let success_criteria = true; + // while 루프의 양자 등가물은 repeat-until-success 루프입니다. + // 양자 컴퓨팅의 확률적 특성으로 인해 때로는 + // 특정 작업 시퀀스를 반복하고 싶을 수 있습니다 + // 특정 조건이 달성될 때까지; 이 루프를 사용하여 이를 표현할 수 있습니다. + repeat { + // 여기에 작업 + } until (success_criteria) // 상태가 도달했는지 확인하기 위한 측정일 수 있습니다 + fixup { + // 필요한 경우 초기 조건으로 재설정 + } +} + +///////////////////////////////////// +// 4. 모두 합치기 + +// Q# 코드는 연산과 함수로 작성됩니다 +operation ApplyXGate(source : Qubit) : Unit { + X(source); +} + +// 연산이 단일 변환을 구현하는 경우 +// 해당 연산의 인접 및 제어된 변형을 정의할 수 있습니다. +// 가장 쉬운 방법은 Unit 뒤에 "is Adj + Ctl"을 추가하는 것입니다. +// 이것은 컴파일러에 변형을 자동으로 생성하도록 지시합니다. +operation ApplyXGateCA(source : Qubit) : Unit is Adj + Ctl { + X(source); +} + +// 이제 Adjoint ApplyXGateCA 및 Controlled ApplyXGateCA를 호출할 수 있습니다. + + +// Q# 코드를 실행하려면 먼저 실행하려는 연산 앞에 @EntryPoint()를 넣을 수 있습니다 +operation XGateDemo() : Unit { + use q = Qubit(); + ApplyXGate(q); +} + +// 간단한 예: 양자 난수 생성기입니다. +// 양자 코드를 사용하여 클래식 난수 비트 배열을 생성합니다. +// `Main`이라는 이름의 호출 가능 항목(함수 또는 연산)은 진입점으로 사용됩니다. +operation Main() : Unit { + mutable bits = [0, size = 5]; // 비트를 저장하는 데 사용할 배열 + use q = Qubit(); + { + // 큐비트 할당 + for i in 0..4 { + // 각 비트를 독립적으로 생성 + H(q); // Hadamard 게이트는 동일한 중첩을 설정합니다 + let result = M(q); // 큐비트 측정은 50/50 확률로 0|1을 얻습니다 + let bit = result == Zero ? 0 | 1; // 측정 결과를 정수로 변환 + set bits w/= i <- bit; // 생성된 비트를 배열에 씁니다 + } + } + Message($"{bits}"); // 결과 인쇄 +} +``` + + +## 더 읽을거리 + +Quantum Katas ([저장소](https://github.com/microsoft/qsharp/tree/main/katas) [호스팅된 튜토리얼](https://quantum.microsoft.com/en-us/tools/quantum-katas))는 양자 컴퓨팅과 Q#을 배우기 위한 훌륭한 자습형 튜토리얼과 프로그래밍 연습을 제공합니다. + +[Q# 문서](https://docs.microsoft.com/quantum/)는 언어 참조 및 사용자 가이드를 포함한 공식 Q# 문서입니다. diff --git a/ko/qt.md b/ko/qt.md new file mode 100644 index 0000000000..d5c4669dd1 --- /dev/null +++ b/ko/qt.md @@ -0,0 +1,159 @@ +--- +category: framework +name: Qt +filename: learnqt.cpp +contributors: + - ["Aleksey Kholovchuk", "https://github.com/vortexxx192"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +**Qt**는 다양한 소프트웨어 및 하드웨어 플랫폼에서 코드 변경이 거의 또는 전혀 없이 실행될 수 있는 크로스 플랫폼 소프트웨어 개발을 위한 널리 알려진 프레임워크이며, 네이티브 애플리케이션의 성능과 속도를 가지고 있습니다. **Qt**는 원래 *C++*로 작성되었지만, *[PyQt](../pyqt/)*, *QtRuby*, *PHP-Qt* 등 다른 언어로도 포팅되었습니다. + +**Qt**는 그래픽 사용자 인터페이스(GUI)가 있는 애플리케이션을 만드는 데 훌륭합니다. 이 튜토리얼은 *C++*에서 수행하는 방법입니다. + +```c++ +/* + * 고전적으로 시작하겠습니다 + */ + +// Qt 프레임워크의 모든 헤더는 대문자 'Q'로 시작합니다 +#include +#include + +int main(int argc, char *argv[]) { + // 애플리케이션 전체 리소스를 관리하는 객체 생성 + QApplication app(argc, argv); + + // 라인 편집 위젯을 만들고 화면에 표시 + QLineEdit lineEdit("Hello world!"); + lineEdit.show(); + + // 애플리케이션의 이벤트 루프 시작 + return app.exec(); +} +``` + +**Qt**의 GUI 관련 부분은 모두 *위젯*과 그들 사이의 *연결*에 관한 것입니다. + +[위젯에 대해 더 알아보기](http://doc.qt.io/qt-5/qtwidgets-index.html) + +```c++ +/* + * 레이블과 버튼을 만들어 봅시다. + * 버튼을 누르면 레이블이 나타나야 합니다. + * + * Qt 코드는 그 자체로 말합니다. + */ + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + QDialog dialogWindow; + dialogWindow.show(); + + // 수직 레이아웃 추가 + QVBoxLayout layout; + dialogWindow.setLayout(&layout); + + QLabel textLabel("Thanks for pressing that button"); + layout.addWidget(&textLabel); + textLabel.hide(); + + QPushButton button("Press me"); + layout.addWidget(&button); + + // 버튼을 누르면 숨겨진 레이블 표시 + QObject::connect(&button, &QPushButton::pressed, + &textLabel, &QLabel::show); + + return app.exec(); +} +``` + +*QObject::connect* 부분을 주목하십시오. 이 메서드는 한 객체의 *시그널*을 다른 객체의 *슬롯*에 연결하는 데 사용됩니다. + +**시그널**은 사용자가 QPushButton 객체를 누를 때 *pressed* 시그널이 방출되는 것과 같이 객체에 특정 이벤트가 발생할 때 방출됩니다. + +**슬롯**은 수신된 시그널에 대한 응답으로 수행될 수 있는 *액션*입니다. + +[슬롯과 시그널에 대해 더 알아보기](http://doc.qt.io/qt-5/signalsandslots.html) + + +다음으로, 표준 위젯을 사용할 뿐만 아니라 상속을 사용하여 동작을 확장할 수 있다는 것을 배워봅시다. 버튼을 만들고 몇 번 눌렸는지 세어 봅시다. 이를 위해 자체 클래스 *CounterLabel*을 정의합니다. 특정 Qt 아키텍처 때문에 별도의 파일에 선언해야 합니다. + +```c++ +// counterlabel.hpp + +#ifndef COUNTERLABEL +#define COUNTERLABEL + +#include + +class CounterLabel : public QLabel { + Q_OBJECT // 모든 사용자 정의 위젯에 있어야 하는 Qt 정의 매크로 + +public: + CounterLabel() : counter(0) { + setText("Counter has not been increased yet"); // QLabel의 메서드 + } + +public slots: + // 버튼 누름에 대한 응답으로 호출될 액션 + void increaseCounter() { + setText(QString("Counter value: %1").arg(QString::number(++counter))); + } + +private: + int counter; +}; + +#endif // COUNTERLABEL +``` + +```c++ +// main.cpp +// 이전 예제와 거의 동일 + +#include +#include +#include +#include +#include +#include "counterlabel.hpp" + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + + QDialog dialogWindow; + dialogWindow.show(); + + QVBoxLayout layout; + dialogWindow.setLayout(&layout); + + CounterLabel counterLabel; + layout.addWidget(&counterLabel); + + QPushButton button("Push me once more"); + layout.addWidget(&button); + QObject::connect(&button, &QPushButton::pressed, + &counterLabel, &CounterLabel::increaseCounter); + + return app.exec(); +} +``` + +이게 다입니다! 물론 Qt 프레임워크는 이 튜토리얼에서 다룬 부분보다 훨씬 더 크므로 읽고 연습할 준비를 하십시오. + +## 더 읽을거리 + +- [Qt 4.8 튜토리얼](http://doc.qt.io/qt-4.8/tutorials.html) +- [Qt 5 튜토리얼](http://doc.qt.io/qt-5/qtexamplesandtutorials.html) + +행운을 빌며 재미있게 보내세요! diff --git a/ko/r.md b/ko/r.md new file mode 100644 index 0000000000..84ae93c38d --- /dev/null +++ b/ko/r.md @@ -0,0 +1,804 @@ +--- +name: R +contributors: + - ["e99n09", "http://github.com/e99n09"] + - ["isomorphismes", "http://twitter.com/isomorphisms"] + - ["kalinn", "http://github.com/kalinn"] + - ["mribeirodantas", "http://github.com/mribeirodantas"] +filename: learnr.r +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +R은 통계 컴퓨팅 언어입니다. 데이터 세트를 업로드하고 정리하고, 통계 절차를 실행하고, 그래프를 만드는 데 많은 라이브러리가 있습니다. LaTeX 문서 내에서 `R` 명령을 실행할 수도 있습니다. + +```r +# 주석은 해시 기호, 즉 숫자 기호(#)로 시작합니다. + +# 여러 줄 주석을 만들 수는 없지만, +# 다음과 같이 여러 주석을 쌓을 수 있습니다. + +# Windows에서는 CTRL-ENTER를 사용하여 한 줄을 실행할 수 있습니다. +# Mac에서는 COMMAND-ENTER입니다. + + + +############################################################################# +# 프로그래밍에 대해 아무것도 이해하지 않고도 할 수 있는 것들 +############################################################################# + +# 이 섹션에서는 프로그래밍에 대해 아무것도 이해하지 않고도 +# R에서 할 수 있는 멋진 것들을 보여줍니다. 코드가 +# 하는 모든 것을 이해하려고 걱정하지 마십시오. 그냥 즐기세요! + +data() # 미리 로드된 데이터 세트 찾아보기 +data(rivers) # 이것을 가져옵니다: "주요 북미 강의 길이" +ls() # 이제 작업 공간에 "rivers"가 나타나는 것을 확인하십시오 +head(rivers) # 데이터 세트 살짝 보기 +# 735 320 325 392 524 450 + +length(rivers) # 몇 개의 강이 측정되었나요? +# 141 +summary(rivers) # 몇 가지 요약 통계는 무엇인가요? +# Min. 1st Qu. Median Mean 3rd Qu. Max. +# 135.0 310.0 425.0 591.2 680.0 3710.0 + +# 줄기-잎 그림 만들기 (히스토그램과 유사한 데이터 시각화) +stem(rivers) + +# 소수점은 |의 오른쪽으로 2자리입니다. +# +# 0 | 4 +# 2 | 011223334555566667778888899900001111223333344455555666688888999 +# 4 | 111222333445566779001233344567 +# 6 | 000112233578012234468 +# 8 | 045790018 +# 10 | 04507 +# 12 | 1471 +# 14 | 56 +# 16 | 7 +# 18 | 9 +# 20 | +# 22 | 25 +# 24 | 3 +# 26 | +# 28 | +# 30 | +# 32 | +# 34 | +# 36 | 1 + +stem(log(rivers)) # 데이터가 정규 분포도 아니고 로그-정규 분포도 아님을 주목하십시오! +# 벨 곡선 근본주의자들아, 받아라. + +# 소수점은 |의 왼쪽으로 1자리입니다. +# +# 48 | 1 +# 50 | +# 52 | 15578 +# 54 | 44571222466689 +# 56 | 023334677000124455789 +# 58 | 00122366666999933445777 +# 60 | 122445567800133459 +# 62 | 112666799035 +# 64 | 00011334581257889 +# 66 | 003683579 +# 68 | 0019156 +# 70 | 079357 +# 72 | 89 +# 74 | 84 +# 76 | 56 +# 78 | 4 +# 80 | +# 82 | 2 + +# 히스토그램 만들기: +hist(rivers, col = "#333333", border = "white", breaks = 25) +hist(log(rivers), col = "#333333", border = "white", breaks = 25) +# 이 매개변수들을 가지고 놀아보세요. 나중에 더 많은 플로팅을 할 것입니다. + +# 미리 로드된 또 다른 멋진 데이터 세트입니다. R에는 이런 것들이 많이 있습니다. +data(discoveries) +plot(discoveries, col = "#333333", lwd = 3, xlab = "Year", + main="Number of important discoveries per year") +plot(discoveries, col = "#333333", lwd = 3, type = "h", xlab = "Year", + main="Number of important discoveries per year") + +# 기본 순서(연도별)를 그대로 두는 대신, +# 정렬하여 일반적인 것을 볼 수도 있습니다: +sort(discoveries) +# [1] 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 +# [26] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 +# [51] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 +# [76] 4 4 4 4 5 5 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 8 9 10 12 + +stem(discoveries, scale = 2) +# +# 소수점은 |에 있습니다. +# +# 0 | 000000000 +# 1 | 000000000000 +# 2 | 00000000000000000000000000 +# 3 | 00000000000000000000 +# 4 | 000000000000 +# 5 | 0000000 +# 6 | 000000 +# 7 | 0000 +# 8 | 0 +# 9 | 0 +# 10 | 0 +# 11 | +# 12 | 0 + +max(discoveries) +# 12 +summary(discoveries) +# Min. 1st Qu. Median Mean 3rd Qu. Max. +# 0.0 2.0 3.0 3.1 4.0 12.0 + +# 주사위를 몇 번 굴리기 +round(runif(7, min = .5, max = 6.5)) +# 1 4 6 1 4 6 4 +# random.seed(31337)을 동일하게 설정하지 않으면 제 숫자와 다를 것입니다. + +# 표준 가우시안에서 9번 추출 +rnorm(9) +# [1] 0.07528471 1.03499859 1.34809556 -0.82356087 0.61638975 -1.88757271 +# [7] -0.59975593 0.57629164 1.08455362 + + + +################################################## +# 데이터 유형 및 기본 산술 +################################################## + +# 이제 프로그래밍 지향적인 튜토리얼 부분입니다. +# 이 섹션에서는 R의 중요한 데이터 유형을 만날 것입니다: +# 정수, 숫자, 문자, 논리 및 요인. +# 다른 것들도 있지만, 이것들은 시작하는 데 필요한 +# 최소한의 것입니다. + +# 정수 +# 긴 저장 정수는 L로 작성됩니다. +5L # 5 +class(5L) # "integer" +# (class() 함수에 대한 자세한 내용은 ?class를 시도하십시오.) +# R에서는 5L과 같은 모든 단일 값이 길이 1의 벡터로 간주됩니다. +length(5L) # 1 +# 길이가 1보다 큰 정수 벡터도 가질 수 있습니다: +c(4L, 5L, 8L, 3L) # 4 5 8 3 +length(c(4L, 5L, 8L, 3L)) # 4 +class(c(4L, 5L, 8L, 3L)) # "integer" + +# 숫자 +# "숫자"는 배정밀도 부동 소수점 숫자입니다. +5 # 5 +class(5) # "numeric" +# 다시 말하지만, R의 모든 것은 벡터입니다. +# 하나 이상의 요소가 있는 숫자 벡터를 만들 수 있습니다. +c(3, 3, 3, 2, 2, 1) # 3 3 3 2 2 1 +# 과학적 표기법도 사용할 수 있습니다. +5e4 # 50000 +6.02e23 # 아보가드로 수 +1.6e-35 # 플랑크 길이 +# 무한히 크거나 작은 숫자도 가질 수 있습니다. +class(Inf) # "numeric" +class(-Inf) # "numeric" +# 예를 들어 integrate(dnorm, 3, Inf)에서 "Inf"를 사용할 수 있습니다. +# 이것은 Z-점수 표를 불필요하게 만듭니다. + +# 기본 산술 +# 숫자로 산술을 할 수 있습니다. +# 정수와 숫자를 혼합하여 산술을 하면 다른 숫자가 됩니다. +10L + 66L # 76 # 정수 더하기 정수는 정수 +53.2 - 4 # 49.2 # 숫자 빼기 숫자는 숫자 +2.0 * 2L # 4 # 숫자 곱하기 정수는 숫자 +3L / 4 # 0.75 # 정수 나누기 숫자는 숫자 +3 %% 2 # 1 # 두 숫자의 나머지는 다른 숫자 +# 불법적인 산술은 "숫자가 아님"을 산출합니다: +0 / 0 # NaN +class(NaN) # "numeric" +# 길이가 1보다 큰 두 벡터에 대해 산술을 할 수 있습니다. +# 더 큰 벡터의 길이가 더 작은 벡터의 정수배인 한 +c(1, 2, 3) + c(1, 2, 3) # 2 4 6 +# 단일 숫자는 길이 1의 벡터이므로 스칼라는 +# 벡터에 요소별로 적용됩니다. +(4 * c(1, 2, 3) - 2) / 2 # 1 3 5 +# 스칼라를 제외하고 길이가 다른 벡터에 대해 산술을 수행할 때는 +# 주의하십시오. 가능하지만, +c(1, 2, 3, 1, 2, 3) * c(1, 2) # 1 4 3 2 2 6 +# 길이를 맞추는 것이 대부분의 경우 더 나은 관행이며 읽기 쉽습니다. +c(1, 2, 3, 1, 2, 3) * c(1, 2, 1, 2, 1, 2) # 1 4 3 2 2 6 + +# 문자 +# R에서는 문자열과 문자 사이에 차이가 없습니다. +"Horatio" # "Horatio" +class("Horatio") # "character" +class("H") # "character" +# 이것들은 모두 길이 1의 문자 벡터였습니다. +# 더 긴 것입니다: +c("alef", "bet", "gimmel", "dalet", "he") +# => "alef" "bet" "gimmel" "dalet" "he" +length(c("Call","me","Ishmael")) # 3 +# 문자 벡터에 대해 정규식 연산을 할 수 있습니다: +substr("Fortuna multis dat nimis, nulli satis.", 9, 15) # "multis " +gsub('u', 'ø', "Fortuna multis dat nimis, nulli satis.") # "Fortøna møltis dat nimis, nølli satis." +# R에는 여러 내장 문자 벡터가 있습니다: +letters +# => +# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" +# [20] "t" "u" "v" "w" "x" "y" "z" +month.abb # "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec" + +# 논리 +# R에서 "논리"는 부울입니다. + +class(TRUE) # "logical" +class(FALSE) # "logical" +# 그들의 행동은 정상입니다. +TRUE == TRUE # TRUE +TRUE == FALSE # FALSE +FALSE != FALSE # FALSE +FALSE != TRUE # TRUE +# 누락된 데이터(NA)도 논리적입니다. +class(NA) # "logical" +# 논리 연산에는 |와 &를 사용하십시오. +# OR +TRUE | FALSE # TRUE +# AND +TRUE & FALSE # FALSE +# 벡터에 |와 &를 적용하면 요소별 논리 연산이 반환됩니다. +c(TRUE, FALSE, FALSE) | c(FALSE, TRUE, FALSE) # TRUE TRUE FALSE +c(TRUE, FALSE, TRUE) & c(FALSE, TRUE, TRUE) # FALSE FALSE TRUE +# x가 TRUE인지 테스트할 수 있습니다. +isTRUE(TRUE) # TRUE +# 여기서는 많은 요소가 있는 논리 벡터를 얻습니다: +c("Z", "o", "r", "r", "o") == "Zorro" # FALSE FALSE FALSE FALSE FALSE +c("Z", "o", "r", "r", "o") == "Z" # TRUE FALSE FALSE FALSE FALSE + +# 요인 +# 요인 클래스는 범주형 데이터용입니다. +# 요인은 순서가 있거나(학년과 같이) 순서가 없을 수 있습니다(색상과 같이). +factor(c("blue", "blue", "green", NA, "blue")) +# blue blue green blue +# Levels: blue green +# "수준"은 범주형 데이터가 가질 수 있는 값입니다. +# 누락된 데이터는 수준에 들어가지 않습니다. +levels(factor(c("green", "green", "blue", NA, "blue"))) # "blue" "green" +# 요인 벡터의 길이가 1이면 수준의 길이도 1입니다. +length(factor("green")) # 1 +length(levels(factor("green"))) # 1 +# 요인은 데이터 프레임에서 흔히 볼 수 있으며, 나중에 다룰 데이터 구조입니다. +data(infert) # "자연 및 유도 유산 후 불임" +levels(infert$education) # "0-5yrs" "6-11yrs" "12+ yrs" + +# NULL +# "NULL"은 이상한 것입니다. 벡터를 "비우는" 데 사용하십시오. +class(NULL) # NULL +parakeet = c("beak", "feathers", "wings", "eyes") +parakeet # "beak" "feathers" "wings" "eyes" +parakeet <- NULL +parakeet # NULL + +# 유형 강제 변환 +# 유형 강제 변환은 값을 다른 유형으로 강제로 변환하는 것입니다. +as.character(c(6, 8)) # "6" "8" +as.logical(c(1,0,1,1)) # TRUE FALSE TRUE TRUE +# 다른 유형의 요소를 벡터에 넣으면 이상한 강제 변환이 발생합니다: +c(TRUE, 4) # 1 4 +c("dog", TRUE, 4) # "dog" "TRUE" "4" +as.numeric("Bilbo") +# => +# [1] NA +# Warning message: +# NAs introduced by coercion + +# 또한 참고: 이것들은 단지 기본 데이터 유형이었습니다. +# 날짜, 시계열 등과 같은 더 많은 데이터 유형이 있습니다. + + + +################################################## +# 변수, 루프, if/else +################################################## + +# 변수는 나중에 사용하기 위해 값을 저장하는 상자와 같습니다. +# 우리는 이것을 변수에 값을 "할당"한다고 합니다. +# 변수가 있으면 루프, 함수 및 if/else 문을 작성할 수 있습니다. + +# 변수 +# 할당하는 방법은 여러 가지가 있습니다: +x = 5 # 가능합니다 +y <- "1" # 전통적으로 선호됩니다 +TRUE -> z # 작동하지만 이상합니다 +# 그들의 행동과 선호도에 대해서는 인터넷을 참조하십시오. + +# 루프 +# for 루프가 있습니다. +for (i in 1:4) { + print(i) +} +# while 루프가 있습니다. +a <- 10 +while (a > 4) { + cat(a, "...", sep = "") + a <- a - 1 +} +# R에서는 for 및 while 루프가 느리게 실행된다는 점을 명심하십시오. +# 전체 벡터(즉, 전체 행, 전체 열)에 대한 연산 +# 또는 apply() 유형 함수(나중에 논의할 것)가 선호됩니다. + +# IF/ELSE +# 다시 말하지만, 꽤 표준적입니다. +if (4 > 3) { + print("4 is greater than 3") +} else { + print("4 is not greater than 3") +} +# => +# [1] "4 is greater than 3" + +# 함수 +# 다음과 같이 정의됩니다: +jiggle <- function(x) { + x = x + rnorm(1, sd=.1) # 약간의 (제어된) 노이즈 추가 + return(x) +} +# 다른 R 함수와 마찬가지로 호출됩니다: +jiggle(5) # 5±ε. set.seed(2716057) 후, jiggle(5)==5.005043 + + + +########################################################################### +# 데이터 구조: 벡터, 행렬, 데이터 프레임 및 배열 +########################################################################### + +# 1차원 + +# 맨 처음부터, 그리고 이미 알고 있는 것부터 시작합시다: 벡터. +vec <- c(8, 9, 10, 11) +vec # 8 9 10 11 +# 대괄호로 부분 집합을 만들어 특정 요소를 요청합니다. +# (R은 1부터 계산을 시작합니다) +vec[1] # 8 +letters[18] # "r" +LETTERS[13] # "M" +month.name[9] # "September" +c(6, 8, 7, 5, 3, 0, 9)[3] # 7 +# 특정 구성 요소의 인덱스를 검색할 수도 있습니다. +which(vec %% 2 == 0) # 1 3 +# 벡터의 처음 또는 마지막 몇 개 항목만 가져옵니다. +head(vec, 1) # 8 +tail(vec, 2) # 10 11 +# 또는 특정 값이 벡터에 있는지 확인합니다. +any(vec == 10) # TRUE +# 인덱스가 "넘어가면" NA를 얻습니다: +vec[6] # NA +# length()로 벡터의 길이를 찾을 수 있습니다. +length(vec) # 4 +# 전체 벡터 또는 벡터의 부분 집합에 대해 연산을 수행할 수 있습니다. +vec * 4 # 32 36 40 44 +vec[2:3] * 5 # 45 50 +any(vec[2:3] == 8) # FALSE +# 그리고 R에는 벡터를 요약하는 많은 내장 함수가 있습니다. +mean(vec) # 9.5 +var(vec) # 1.666667 +sd(vec) # 1.290994 +max(vec) # 11 +min(vec) # 8 +sum(vec) # 38 +# 더 멋진 내장 함수: +5:15 # 5 6 7 8 9 10 11 12 13 14 15 +seq(from = 0, to = 31337, by = 1337) +# => +# [1] 0 1337 2674 4011 5348 6685 8022 9359 10696 12033 13370 14707 +# [13] 16044 17381 18718 20055 21392 22729 24066 25403 26740 28077 29414 30751 + +# 2차원 (모두 한 클래스) + +# 다음과 같이 동일한 유형의 항목으로 행렬을 만들 수 있습니다: +mat <- matrix(nrow = 3, ncol = 2, c(1, 2, 3, 4, 5, 6)) +mat +# => +# [,1] [,2] +# [1,] 1 4 +# [2,] 2 5 +# [3,] 3 6 +# 벡터와 달리 행렬의 클래스는 내용에 관계없이 "matrix"입니다. +class(mat) # "matrix" "array" +# 첫 번째 행 요청 +mat[1, ] # 1 4 +# 첫 번째 열에 대한 연산 수행 +3 * mat[, 1] # 3 6 9 +# 특정 셀 요청 +mat[3, 2] # 6 + +# 전체 행렬 전치 +t(mat) +# => +# [,1] [,2] [,3] +# [1,] 1 2 3 +# [2,] 4 5 6 + +# 행렬 곱셈 +mat %*% t(mat) +# => +# [,1] [,2] [,3] +# [1,] 17 22 27 +# [2,] 22 29 36 +# [3,] 27 36 45 + +# cbind()는 벡터를 열 단위로 붙여 행렬을 만듭니다. +mat2 <- cbind(1:4, c("dog", "cat", "bird", "dog")) +mat2 +# => +# [,1] [,2] +# [1,] "1" "dog" +# [2,] "2" "cat" +# [3,] "3" "bird" +# [4,] "4" "dog" +class(mat2) # matrix +# 다시 말하지만, 무슨 일이 일어났는지 주목하십시오! +# 행렬은 모두 동일한 클래스의 항목을 포함해야 하므로, +# 모든 것이 문자 클래스로 변환되었습니다. +c(class(mat2[, 1]), class(mat2[, 2])) + +# rbind()는 벡터를 행 단위로 붙여 행렬을 만듭니다. +mat3 <- rbind(c(1, 2, 4, 5), c(6, 7, 0, 4)) +mat3 +# => +# [,1] [,2] [,3] [,4] +# [1,] 1 2 4 5 +# [2,] 6 7 0 4 +# 아, 모두 같은 클래스입니다. 강제 변환이 없습니다. 훨씬 낫습니다. + +# 2차원 (다른 클래스) + +# 다른 유형의 열에는 데이터 프레임을 사용하십시오. +# 이 데이터 구조는 통계 프로그래밍에 매우 유용하며, +# "pandas" 패키지의 Python에 버전이 추가되었습니다. + +students <- data.frame(c("Cedric", "Fred", "George", "Cho", "Draco", "Ginny"), + c( 3, 2, 2, 1, 0, -1), + c( "H", "G", "G", "R", "S", "G")) +names(students) <- c("name", "year", "house") # 열 이름 지정 +class(students) # "data.frame" +students +# => +# name year house +# 1 Cedric 3 H +# 2 Fred 2 G +# 3 George 2 G +# 4 Cho 1 R +# 5 Draco 0 S +# 6 Ginny -1 G +class(students$year) # "numeric" +class(students[,3]) # "factor" +# 차원 찾기 +nrow(students) # 6 +ncol(students) # 3 +dim(students) # 6 3 +# data.frame() 함수는 기본적으로 문자 벡터를 요인 벡터로 +# 변환하는 데 사용되었습니다. 이것은 R 4.0.0에서 변경되었습니다. +# R 버전이 더 오래된 경우 data.frame을 만들 때 +# stringsAsFactors = FALSE를 설정하여 이 기능을 끄십시오. +?data.frame + +# 미묘하게 다른 데이터 프레임을 부분 집합으로 만드는 여러 가지 방법이 있습니다. +students$year # 3 2 2 1 0 -1 +students[, 2] # 3 2 2 1 0 -1 +students[, "year"] # 3 2 2 1 0 -1 + +# data.frame 구조의 증강 버전은 data.table입니다. +# 거대하거나 패널 데이터를 다루거나 몇 개의 데이터 세트를 병합해야 하는 경우 +# data.table이 좋은 선택이 될 수 있습니다. 다음은 간략한 둘러보기입니다: +install.packages("data.table") # CRAN에서 패키지 다운로드 +require(data.table) # 로드 +students <- as.data.table(students) +students # 약간 다른 출력을 주목하십시오 +# => +# name year house +# 1: Cedric 3 H +# 2: Fred 2 G +# 3: George 2 G +# 4: Cho 1 R +# 5: Draco 0 S +# 6: Ginny -1 G +students[name == "Ginny"] # 이름이 "Ginny"인 행 가져오기 +# => +# name year house +# 1: Ginny -1 G +students[year == 2] # 연도가 2인 행 가져오기 +# => +# name year house +# 1: Fred 2 G +# 2: George 2 G +# data.table은 두 데이터 세트를 병합하기 쉽습니다. +# 학생들과 병합할 다른 data.table을 만들어 봅시다. +founders <- data.table(house = c("G" , "H" , "R" , "S"), + founder = c("Godric", "Helga", "Rowena", "Salazar")) +founders +# => +# house founder +# 1: G Godric +# 2: H Helga +# 3: R Rowena +# 4: S Salazar +setkey(students, house) +setkey(founders, house) +students <- founders[students] # "house"를 일치시켜 두 데이터 세트 병합 +setnames(students, c("house", "houseFounderName", "studentName", "year")) +students[, order(c("name", "year", "house", "houseFounderName")), with = F] +# => +# studentName year house houseFounderName +# 1: Fred 2 G Godric +# 2: George 2 G Godric +# 3: Ginny -1 G Godric +# 4: Cedric 3 H Helga +# 5: Cho 1 R Rowena +# 6: Draco 0 S Salazar + +# data.table은 요약 테이블을 쉽게 만듭니다. +students[, sum(year), by = house] +# => +# house V1 +# 1: G 3 +# 2: H 3 +# 3: R 1 +# 4: S 0 + +# data.frame 또는 data.table에서 열을 삭제하려면 +# NULL 값을 할당하십시오. +students$houseFounderName <- NULL +students +# => +# studentName year house +# 1: Fred 2 G +# 2: George 2 G +# 3: Ginny -1 G +# 4: Cedric 3 H +# 5: Cho 1 R +# 6: Draco 0 S + +# 부분 집합으로 행 삭제 +# data.table 사용: +students[studentName != "Draco"] +# => +# house studentName year +# 1: G Fred 2 +# 2: G George 2 +# 3: G Ginny -1 +# 4: H Cedric 3 +# 5: R Cho 1 +# data.frame 사용: +students <- as.data.frame(students) +students[students$house != "G", ] +# => +# house houseFounderName studentName year +# 4 H Helga Cedric 3 +# 5 R Rowena Cho 1 +# 6 S Salazar Draco 0 + +# 다차원 (모든 요소가 한 유형) + +# 배열은 n차원 테이블을 만듭니다. +# 모든 요소는 동일한 유형이어야 합니다. +# 2차원 테이블(행렬과 유사)을 만들 수 있습니다. +array(c(c(1, 2, 4, 5), c(8, 9, 3, 6)), dim = c(2, 4)) +# => +# [,1] [,2] [,3] [,4] +# [1,] 1 4 8 3 +# [2,] 2 5 9 6 +# 배열을 사용하여 3차원 행렬도 만들 수 있습니다. +array(c(c(c(2, 300, 4), c(8, 9, 0)), c(c(5, 60, 0), c(66, 7, 847))), dim = c(3, 2, 2)) +# => +# , , 1 +# +# [,1] [,2] +# [1,] 2 8 +# [2,] 300 9 +# [3,] 4 0 +# +# , , 2 +# +# [,1] [,2] +# [1,] 5 66 +# [2,] 60 7 +# [3,] 0 847 + +# 리스트 (다차원, 들쭉날쭉할 수 있음, 다른 유형) + +# 마지막으로 R에는 리스트(벡터의)가 있습니다. +list1 <- list(time = 1:40) +list1$price = c(rnorm(40, .5*list1$time, 4)) # 무작위 +list1 +# 다음과 같이 리스트에서 항목을 가져올 수 있습니다. +list1$time # 한 가지 방법 +list1[["time"]] # 다른 방법 +list1[[1]] # 또 다른 방법 +# => +# [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 +# [34] 34 35 36 37 38 39 40 +# 다른 벡터와 마찬가지로 리스트 항목을 부분 집합으로 만들 수 있습니다. +list1$price[4] + +# 리스트는 R에서 작업하기에 가장 효율적인 데이터 구조가 아닙니다. +# 아주 좋은 이유가 없다면 data.frames를 사용하는 것이 좋습니다. +# 리스트는 종종 선형 회귀를 수행하는 함수에서 반환됩니다. + +################################################## +# apply() 계열 함수 +################################################## + +# mat를 기억하시나요? +mat +# => +# [,1] [,2] +# [1,] 1 4 +# [2,] 2 5 +# [3,] 3 6 +# apply(X, MARGIN, FUN)을 사용하여 행(MAR = 1) 또는 열(MAR = 2)에 대해 +# 함수 FUN을 행렬 X에 적용합니다. +# 즉, R은 X의 각 행(또는 열)에 대해 FUN을 수행하며, +# for 또는 while 루프보다 훨씬 빠릅니다. +apply(mat, MAR = 2, jiggle) +# => +# [,1] [,2] +# [1,] 3 15 +# [2,] 7 19 +# [3,] 11 23 +# 다른 함수: ?lapply, ?sapply + +# 너무 겁먹지 마세요. 모두가 그것들이 다소 혼란스럽다는 데 동의합니다. + +# plyr 패키지는 *apply() 계열을 대체(및 개선!)하는 것을 목표로 합니다. +install.packages("plyr") +require(plyr) +?plyr + + + +######################### +# 데이터 로드 +######################### + +# "pets.csv"는 인터넷에 있는 파일입니다. +# (하지만 컴퓨터에 있는 파일일 수도 있습니다) +require(RCurl) +pets <- read.csv(textConnection(getURL("https://learnxinyminutes.com/pets.csv"))) +pets +head(pets, 2) # 처음 두 행 +tail(pets, 1) # 마지막 행 + +# 데이터 프레임 또는 행렬을 .csv 파일로 저장하려면 +write.csv(pets, "pets2.csv") # 새 .csv 파일 만들기 +# setwd()로 작업 디렉토리 설정, getwd()로 조회 + +# 자세한 내용은 ?read.csv 및 ?write.csv를 참조하십시오. + + + +######################### +# 통계 분석 +######################### + +# 선형 회귀! +linearModel <- lm(price ~ time, data = list1) +linearModel # 회귀 결과 출력 +# => +# Call: +# lm(formula = price ~ time, data = list1) +# +# Coefficients: +# (Intercept) time +# 0.1453 0.4943 +summary(linearModel) # 회귀에서 더 자세한 출력 +# => +# Call: +# lm(formula = price ~ time, data = list1) +# +# Residuals: +# Min 1Q Median 3Q Max +# -8.3134 -3.0131 -0.3606 2.8016 10.3992 +# +# Coefficients: +# Estimate Std. Error t value Pr(>|t|) +# (Intercept) 0.14527 1.50084 0.097 0.923 +# time 0.49435 0.06379 7.749 2.44e-09 *** +# --- +# Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 +# +# Residual standard error: 4.657 on 38 degrees of freedom +# Multiple R-squared: 0.6124, Adjusted R-squared: 0.6022 +# F-statistic: 60.05 on 1 and 38 DF, p-value: 2.44e-09 +coef(linearModel) # 추정된 매개변수 추출 +# => +# (Intercept) time +# 0.1452662 0.4943490 +summary(linearModel)$coefficients # 결과를 추출하는 다른 방법 +# => +# Estimate Std. Error t value Pr(>|t|) +# (Intercept) 0.1452662 1.50084246 0.09678975 9.234021e-01 +# time 0.4943490 0.06379348 7.74920901 2.440008e-09 +summary(linearModel)$coefficients[, 4] # p-값 +# => +# (Intercept) time +# 9.234021e-01 2.440008e-09 + +# 일반화 선형 모델 +# 로지스틱 회귀 +set.seed(1) +list1$success = rbinom(length(list1$time), 1, .5) # 무작위 이진 +glModel <- glm(success ~ time, data = list1, family=binomial(link="logit")) +glModel # 로지스틱 회귀 결과 출력 +# => +# Call: glm(formula = success ~ time, +# family = binomial(link = "logit"), data = list1) +# +# Coefficients: +# (Intercept) time +# 0.17018 -0.01321 +# +# Degrees of Freedom: 39 Total (i.e. Null); 38 Residual +# Null Deviance: 55.35 +# Residual Deviance: 55.12 AIC: 59.12 +summary(glModel) # 회귀에서 더 자세한 출력 +# => +# Call: +# glm( +# formula = success ~ time, +# family = binomial(link = "logit"), +# data = list1) + +# Deviance Residuals: +# Min 1Q Median 3Q Max +# -1.245 -1.118 -1.035 1.202 1.327 +# +# Coefficients: +# Estimate Std. Error z value Pr(>|z|) +# (Intercept) 0.17018 0.64621 0.263 0.792 +# time -0.01321 0.02757 -0.479 0.632 +# +# (Dispersion parameter for binomial family taken to be 1) +# +# Null deviance: 55.352 on 39 degrees of freedom +# Residual deviance: 55.121 on 38 degrees of freedom +# AIC: 59.121 +# +# Number of Fisher Scoring iterations: 3 + + +######################### +# 플롯 +######################### + +# 내장 플로팅 함수 +# 산점도! +plot(list1$time, list1$price, main = "fake data") +# 기존 플롯에 회귀선 플로팅 +abline(linearModel, col = "red") +# 다양한 멋진 진단 얻기 +plot(linearModel) +# 히스토그램! +hist(rpois(n = 10000, lambda = 5), col = "thistle") +# 막대 그래프! +barplot(c(1, 4, 5, 1, 2), names.arg = c("red", "blue", "purple", "green", "yellow")) + +# GGPLOT2 +# 하지만 이것들은 R의 가장 예쁜 플롯조차 아닙니다. +# 더 많고 더 나은 그래픽을 위해 ggplot2 패키지를 사용해 보십시오. +install.packages("ggplot2") +require(ggplot2) +?ggplot2 +pp <- ggplot(students, aes(x = house)) +pp + geom_bar() +ll <- as.data.table(list1) +pp <- ggplot(ll, aes(x = time, price)) +pp + geom_point() +# ggplot2에는 훌륭한 문서가 있습니다 (http://docs.ggplot2.org/current/에서 사용 가능) +``` + +## R을 어떻게 얻나요? + +* R 및 R GUI는 [http://www.r-project.org/](http://www.r-project.org/)에서 얻을 수 있습니다. +* [RStudio](http://www.rstudio.com/ide/)는 또 다른 GUI입니다. diff --git a/ko/racket.md b/ko/racket.md index 4bbeb9c208..d1f66948f0 100644 --- a/ko/racket.md +++ b/ko/racket.md @@ -1,488 +1,243 @@ --- - +name: Paren +filename: learnparen.paren 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"] + - ["Claudson Martins", "https://github.com/claudsonm"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] --- -Racket 은 Lisp/Scheme 계열의 일반 목적의, 다중 패러다임 프로그래밍 언어이다. +[Paren](https://bitbucket.org/ktg/paren)은 Lisp의 방언입니다. 임베디드 언어로 설계되었습니다. -```racket -#lang racket ; 우리가 사용하는 언어를 정의한다. +일부 예제는 [Racket](../racket/)에서 가져왔습니다. +```scheme ;;; 주석 +# 주석 -;; 한 줄 주석은 세미콜론으로 시작한다. - -#| 블록 주석 - 은 여러 줄에 걸칠 수 있으며... - #| - 중첩될 수 있다! - |# -|# - -;; S-expression 주석은 아래 식을 버리므로, -;; 디버깅할 때 식을 주석화할 때 유용하다. -#; (이 식은 버려짐) +;; 한 줄 주석은 세미콜론 또는 샵 기호로 시작합니다. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 1. 근본 자료형과 연산자 +;; 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) -;; 이제, 산술 연산 몇 개 +123 ; 정수 +3.14 ; 더블 +6.02e+23 ; 더블 +(int 3.14) ; => 3 : int +(double 123) ; => 123 : double + +;; 함수 적용은 (f x y z ...)로 작성됩니다. +;; 여기서 f는 함수이고 x, y, z는 피연산자입니다. +;; 글자 그대로의 데이터 목록을 만들려면 (quote)를 사용하여 평가를 중지하십시오. +(quote (+ 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 ; => #\λ - -;;; 문자열은 고정 길이의 문자 배열이다. +(^ 2 3) ; => 8 +(/ 5 2) ; => 2 +(% 5 2) ; => 1 +(/ 5.0 2) ; => 2.5 + +;;; 부울 +true ; 참 +false ; 거짓 -- #f가 아니면 참 +(! true) ; => false +(&& true false (prn "doesn't get here")) ; => false +(|| false true (prn "doesn't get here")) ; => true + +;;; 문자는 int입니다. +(char-at "A" 0) ; => 65 +(chr 65) ; => "A" + +;;; 문자열은 고정 길이의 문자 배열입니다. "Hello, world!" -"Benjamin \"Bugsy\" Siegel" ; 백슬래시는 탈출 문자이다. -"Foo\tbar\41\x21\u0021\a\r\n" ; C 탈출 문자, 유니코드 포함 -"λx:(μα.α→α).xx" ; 유니코드 문자 포함 가능 +"Benjamin \"Bugsy\" Siegel" ; 백슬래시는 이스케이프 문자입니다. +"Foo\tbar\r\n" ; C 이스케이프 포함: \t \r \n -;; 문자열은 붙여질 수 있다! -(string-append "Hello " "world!") ; => "Hello world!" +;; 문자열도 추가할 수 있습니다! +(strcat "Hello " "world!") ; => "Hello world!" -;; 문자열은 문자의 리스트처럼 취급될 수 있다. -(string-ref "Apple" 0) ; => #\A +;; 문자열은 문자 목록처럼 처리될 수 있습니다. +(char-at "Apple" 0) ; => 65 -;; format은 문자열을 형식화하기 위해 사용된다: -(format "~a can be ~a" "strings" "formatted") - -;; 인쇄는 쉽다. -(printf "I'm Racket. Nice to meet you!\n") +;; 인쇄는 매우 쉽습니다. +(pr "I'm" "Paren. ") (prn "Nice to meet you!") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 2. 변수 -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; define으로 변수를 만든다. -;; 변수명으로 다음 문자를 사용할 수 없다: ()[]{}",'`;#|\ -(define some-var 5) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; (set)을 사용하여 변수를 만들거나 설정할 수 있습니다. +;; 변수 이름은 ();#"를 제외한 모든 문자를 사용할 수 있습니다. +(set some-var 5) ; => 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? 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) - -;;; 벡터 +;; 이전에 할당되지 않은 변수에 접근하면 예외가 발생합니다. +; x ; => 알 수 없는 변수: x : nil -;; 벡터는 고정 길이의 배열이다. -#(1 2 3) ; => '#(1 2 3) +;; 지역 바인딩: 'a'와 'b'는 (fn ...) 내에서만 '1'과 '2'에 바인딩됩니다. +((fn (a b) (+ a b)) 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 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; 3. 컬렉션 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 키가 없을 때 반환할 기본값을 지정할 수 있다. -(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)) +;; 목록은 벡터와 같은 데이터 구조입니다. (임의 접근은 O(1)입니다.) +(cons 1 (cons 2 (cons 3 (list)))) ; => (1 2 3) +;; 'list'는 목록에 대한 편리한 가변 생성자입니다. +(list 1 2 3) ; => (1 2 3) +;; 그리고 인용 부호도 리터럴 목록 값에 사용할 수 있습니다. +(quote (+ 1 2)) ; => (+ 1 2) -;; 이 해시들은 불변이라는 점을 기억하시오! -m ; => '#hash((b . 2) (a . 1) (c . 3)) <-- no `d' +;; 'cons'를 사용하여 목록 시작 부분에 항목을 추가할 수 있습니다. +(cons 0 (list 1 2 3)) ; => (0 1 2 3) -;; `hash-remove'로 키를 삭제 (이것도 함수적) -(hash-remove m 'a) ; => '#hash((b . 2) (c . 3)) +;; 목록은 매우 기본적인 유형이므로, 목록에 대한 많은 기능이 있습니다. +;; 몇 가지 예: +(map inc (list 1 2 3)) ; => (2 3 4) +(filter (fn (x) (== 0 (% x 2))) (list 1 2 3 4)) ; => (2 4) +(length (list 1 2 3 4)) ; => 4 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 3. 함수 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; `lambda'로 함수를 만든다. -;; 함수는 항상 마지막 식을 반환한다. -(lambda () "Hello World") ; => # -;; 유니코드 `λ'도 사용 가능 -(λ () "Hello World") ; => same function +;; 'fn'을 사용하여 함수를 만듭니다. +;; 함수는 항상 마지막 표현식의 값을 반환합니다. +(fn () "Hello World") ; => (fn () Hello World) : fn -;; 모든 함수를 호출할 때는 괄호를 쓴다, lambda 식도 포함하여. -((lambda () "Hello World")) ; => "Hello World" -((λ () "Hello World")) ; => "Hello World" +;; 모든 함수를 호출할 때는 괄호를 사용합니다. 람다 표현식도 포함합니다. +((fn () "Hello World")) ; => "Hello World" ;; 변수에 함수를 할당 -(define hello-world (lambda () "Hello World")) +(set hello-world (fn () "Hello World")) (hello-world) ; => "Hello World" -;; 문법적 설탕을 사용하여 함수 정의를 더 짧게할 수 있다: -(define (hello-world2) "Hello World") +;; 함수 정의 구문 설탕을 사용하여 이것을 단축할 수 있습니다: +(defn hello-world2 () "Hello World") -;; 위에서 ()는 함수의 인자 리스트이다. -(define hello - (lambda (name) - (string-append "Hello " name))) +;; 위의 ()는 함수의 인수 목록입니다. +(set hello + (fn (name) + (strcat "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" + +;; ... 또는 동일하게, 설탕이 첨가된 정의를 사용하여: +(defn hello2 (name) + (strcat "Hello " name)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 4. 동등성 +;; 4. 같음 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 숫자에는 `='를 사용하시오. -(= 3 3.0) ; => #t -(= 2 1) ; => #f +;; 숫자의 경우 '=='를 사용합니다. +(== 3 3.0) ; => true +(== 2 1) ; => false -;; 개체의 동등성에는 `eq?'를 사용하시오. -(eq? 3 3) ; => #t -(eq? 3 3.0) ; => #f -(eq? (list 3) (list 3)) ; => #f +;; 객체 동등성의 경우 'eq?'를 사용합니다. +(eq? 3 3) ; => true +(eq? 3 3.0) ; => false +(eq? (list 3) (list 3)) ; => false -;; 모음에는 `equal?'을 사용하시오. -(equal? (list 'a 'b) (list 'a 'b)) ; => #t -(equal? (list 'a 'b) (list 'b 'a)) ; => #f +;; 컬렉션의 경우 'equal?'을 사용합니다. +(equal? (list 'a 'b) (list 'a 'b)) ; => true +(equal? (list 'a 'b) (list 'b 'a)) ; => false ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 5. 흐름 제어하기 +;; 5. 제어 흐름 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; 조건 +;;; 조건문 -(if #t ; 조사 식 - "this is true" ; 그러면 식 - "this is false") ; 아니면 식 +(if true ; 테스트 표현식 + "this is true" ; then 표현식 + "this is false") ; else 표현식 ; => "this is true" -;; 조건에서는 #f가 아니면 참으로 취급된다. +;; 조건에서 #f가 아니면 참으로 처리됩니다. (member 'Groucho '(Harpo Groucho Zeppo)) ; => '(Groucho Zeppo) (if (member 'Groucho '(Harpo Groucho Zeppo)) 'yep 'nope) ; => 'yep -;; `cond'는 연속하여 조사하여 값을 선택한다. +;; `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 루프는 숫자에 대한 것입니다. +;; (for SYMBOL START END STEP EXPR ..) +(for i 0 10 2 (pr i "")) ; => 0 2 4 6 8 10 인쇄 +(for i 0.0 10 2.5 (pr i "")) ; => 0 2.5 5 7.5 10 인쇄 -(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 +;; while 루프 +((fn (i) + (while (< i 10) + (pr i) + (++ i))) 0) ; => 0123456789 인쇄 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 6. 변경 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 기존 변수에 새 값을 할당하려면 `set!'을 사용한다. -(define n 5) -(set! n (add1 n)) +;; 'set'을 사용하여 변수 또는 위치에 새 값을 할당합니다. +(set n 5) ; => 5 +(set n (inc n)) ; => 6 n ; => 6 +(set a (list 1 2)) ; => (1 2) +(set (nth 0 a) 3) ; => 3 +a ; => (3 2) ;; 명시적인 가변 값을 사용하려면 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. 모듈 +;; 7. 매크로 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; 모듈은 코드를 여러 파일과 재사용 가능한 라이브러리로 조직하게 한다. -;; 여기서 우리는 서브-모듈을 사용한다. 이 글이 만드는 전체 모듈("lang" 줄 부터 시작)에 포함된 모듈이다. - -(module cake racket/base ; racket/base 기반의 `cake' 모듈 정의 +;; 매크로는 언어의 구문을 확장할 수 있게 합니다. +;; Paren 매크로는 쉽습니다. +;; 사실, (defn)은 매크로입니다. +(defmacro setfn (name ...) (set name (fn ...))) +(defmacro defn (name ...) (def name (fn ...))) - (provide print-cake) ; 모듈이 노출(export)시키는 함수 +;; 중위 표기법 추가 +(defmacro infix (a op ...) (op a ...)) +(infix 1 + 2 (infix 3 * 4)) ; => 15 - (define (print-cake n) - (show " ~a " n #\.) - (show " .-~a-. " n #\|) - (show " | ~a | " n #\space) - (show "---~a---" n #\-)) +;; 매크로는 위생적이지 않습니다. 기존 변수를 침범할 수 있습니다! +;; 코드 변환입니다. +(define-syntax-rule (swap! x y) ; -!는 변경의 관용구 + (let ([tmp x]) + (set! x y) + (set! y tmp))) - (define (show fmt n ch) ; 내부 함수 - (printf fmt (make-string n ch)) - (newline))) +(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)) -;; `require'를 사용하여 모듈에서 모든 `provide'된 이름을 사용한다. -(require 'cake) ; '는 지역 지역 서브-모듈을 위한 것이다. -(print-cake 3) -; (show "~a" 1 #\A) ; => 에러, `show'가 export되지 않았음 +;; 하지만 그것들은 단지 코드 변형일 뿐입니다. 예를 들어: +(define-syntax-rule (bad-while condition body ...) + (when condition + body ... + (bad-while condition body ...))) +;; 이 매크로는 엉터리다: 무한 코드를 생성하며, +;; 이것을 사용하려고 하면 컴파일러가 무한 반복에 빠진다. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; 8. 클래스와 개체 @@ -532,10 +287,10 @@ vec ; => #(1 2 3 4) ;; 매크로는 언어의 문법을 확장할 수 있게 한다. ;; while 반복문을 추가하자. -(define-syntax-rule (while condition body ...) - (let loop () - (when condition - body ... +(define-syntax-rule (while condition body ...) + (let loop () + (when condition + body ... (loop)))) (let ([i 0]) @@ -553,13 +308,13 @@ vec ; => #(1 2 3 4) (define other 3) (swap! tmp other) (printf "tmp = ~a; other = ~a\n" tmp other) -;; `tmp` 변수는 이름 충돌을 피하기 위해 `tmp_1`로 이름이 변경된다. +;; `tmp` 변수는 이름 충돌을 피하기 위해 `tmp_1`로 이름이 변경된다. ;; (let ([tmp_1 tmp]) ;; (set! tmp other) ;; (set! other tmp_1)) ;; 하지만 그것들은 단지 코드 변형일 뿐이다. 예를 들어: -(define-syntax-rule (bad-while condition body ...) +(define-syntax-rule (bad-while condition body ...) (when condition body ... (bad-while condition body ...))) @@ -634,4 +389,4 @@ vec ; => #(1 2 3 4) ## 더 읽을거리 -더 배우고 싶으면, [Getting Started with Racket](http://docs.racket-lang.org/getting-started/)도 보시오. +더 배우고 싶으면, [Getting Started with Racket](http://docs.racket-lang.org/getting-started/)도 보시오. \ No newline at end of file diff --git a/ko/raku-pod.md b/ko/raku-pod.md new file mode 100644 index 0000000000..7399c2d365 --- /dev/null +++ b/ko/raku-pod.md @@ -0,0 +1,553 @@ +--- +name: Pod +contributors: + - ["Luis F. Uceta", "https://uzluisf.gitlab.io/"] +filename: learnpod.pod6 +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Pod는 표현하기 쉽고 순수하게 기술적인 마크업 언어로, 표현 구성 요소가 없습니다. Raku 프로그램 및 모듈을 문서화하는 데 사용될 뿐만 아니라 언어 문서, 블로그 및 기타 유형의 문서 작성을 위해서도 활용될 수 있습니다. + +Pod 문서는 해당 `Pod::To` 모듈(예: HTML 변환을 위한 `Pod::To::HTML`)의 해당 변형을 사용하여 HTML 및 기타 여러 형식(예: Markdown, Latex, 일반 텍스트 등)으로 쉽게 변환할 수 있습니다. + +- [일반 정보](#general-info) +- [Pod 기본 사항](#pod-basics) + - [기본 텍스트 서식](#basic-text-formatting) + - [제목](#headings) + - [일반 단락](#ordinary-paragraphs) + - [목록](#lists) + - [코드 블록](#code-blocks) + - [주석](#comments) + - [링크](#links) + - [표](#tables) +- [블록 구조](#block-structures) + - [약어 블록](#abbreviated-blocks) + - [구분된 블록](#delimited-blocks) + - [단락 블록](#paragraph-blocks) +- [구성 데이터](#configuration-data) + - [표준 구성 옵션](#standard-configuration-options) + - [블록 사전 구성](#block-pre-configuration) +- [의미 블록](#semantic-blocks) +- [기타](#miscellaneous) + - [참고](#notes) + - [키보드 입력](#keyboard-input) + - [터미널 출력](#terminal-output) + - [유니코드](#unicode) +- [Pod 렌더링](#rendering-pod) +- [Pod 액세스](#accessing-pod) + +## 일반 정보 + +모든 Pod 문서는 `=begin pod`로 시작하고 `=end pod`로 끝나야 합니다. +이 두 구분 기호 사이에 있는 모든 것은 처리되어 +문서 생성에 사용됩니다. + +``` +=begin pod + +매우 간단한 Raku Pod 문서. 다른 모든 지시문은 여기에 있습니다! + +=end pod +``` + +Pod 문서는 일반적으로 Raku 코드와 공존합니다. 단독으로 사용되는 경우 +Pod 파일은 종종 `.pod6` 접미사를 가집니다. 앞으로는 +논의되는 구성이 `=begin pod ... =end pod` +지시문으로 둘러싸여 있다고 가정합니다. + +## Pod 기본 사항 + +### 기본 텍스트 서식 + +텍스트는 `B<>`, `I<>`, `U<>` 및 `C<>`와 같은 서식 코드를 사용하여 굵게, 기울임꼴, 밑줄 또는 그대로(코드 서식용) 쉽게 스타일을 지정할 수 있습니다. + +``` +B<이 텍스트는 굵게 표시됩니다.> + +I<이 텍스트는 기울임꼴로 표시됩니다.> + +U<이 텍스트는 밑줄이 그어져 있습니다.> + +함수 C는 그대로 처리됩니다. +``` + +더 많은 서식 코드(예: `L<>`, `T<>` 등)가 있지만, 문서 전체에서 나중에 논의될 것입니다. 단일 대문자 뒤에 즉시 단일 또는 이중 꺾쇠 괄호가 오는 것을 보면 알 수 있습니다. 꺾쇠 괄호의 유니코드 변형(«»)도 사용할 수 있습니다. + +### 제목 + +제목은 `N`이 제목 수준인 `=headN` 지시문을 사용하여 생성됩니다. + +``` +=head1 레벨 1입니다. +=head2 레벨 2입니다. +=head3 레벨 3입니다. +=head4 레벨 4입니다. +=head5 레벨 5입니다. +=head6 레벨 6입니다. +``` + +### 일반 단락 + +일반 단락은 하나 이상의 인접한 텍스트 줄로 구성되며, 각 줄은 공백이 아닌 문자로 시작합니다. 모든 단락은 첫 번째 빈 줄 또는 블록 지시문으로 종료됩니다. + +``` +=head1 첫 번째 수준 제목 블록 + +=head2 단락 1 + +이것은 일반 단락입니다. 텍스트는 압축되고 +짧은 줄은 채워집니다. 첫 번째 빈 줄에서 종료됩니다. + +=head2 단락 2 + +이것은 더 짧지만 또 다른 일반 단락입니다. +``` + +또는 `=para` 지시문을 사용하여 인접한 텍스트 줄을 단락으로 명시적으로 표시할 수 있습니다. + +``` +=head1 첫 번째 수준 제목 블록 + +=head2 단락 1 + +=para +이것은 일반 단락입니다. 텍스트는 압축되고 +짧은 줄은 채워집니다. 첫 번째 빈 줄에서 종료됩니다. + +=head2 단락 2 + +=para +이것은 더 짧지만 또 다른 일반 단락입니다. +``` + +### 목록 + +순서 없는 목록은 `=item` 지시문을 사용하여 만들 수 있습니다. + +``` +=item 항목 +=item 항목 +=item 다른 항목 +``` + +하위 목록은 `=item1`, `=item2`, `=item3`, `...`, `=itemN` 등과 같은 지시문을 사용하여 각 수준의 항목으로 달성됩니다. `=item` 지시문은 기본적으로 `=item1`입니다. + +``` +=item1 항목 1 +=item1 항목 2 +=item1 항목 3 + =item2 하위 항목 + =item2 하위 항목 +=item1 항목 4 +``` + +용어 또는 명령을 정의하는 정의 목록은 `=defn` 지시문을 사용합니다. +이는 HTML의 `
` 요소와 동일합니다. + +``` +=defn 보드민의 야수 +보드민 무어에 서식하는 큰 고양이과 동물. + +=defn 모르가우르 +바다뱀. + +=defn 올맨 +거대한 올빼미와 같은 생물. +``` + +### 코드 블록 + +코드 블록(HTML `` 요소 사용)은 각 줄을 하나 이상의 공백 문자로 시작하여 생성됩니다. + +``` + #`( 이것은 주석입니다 ) + my $sum = -> $x, $y { $x + $y } + say $sum(12, 5); +``` + +[기본 텍스트 서식](#basic-text-formatting) 섹션에서 보여주듯이, +인라인 코드는 `C<>` 코드를 사용하여 만들 수 있습니다. + +``` +Raku에는 텍스트를 출력하는 여러 함수/메서드가 있습니다. 그 중 일부는 C, C 및 C입니다. +``` + +### 주석 + +Pod 블록은 Rakudo Raku 컴파일러에 의해 무시되지만, Pod 블록으로 식별된 모든 것은 Pod 렌더러에 의해 읽히고 해석됩니다. Pod 블록이 렌더러에 의해 렌더링되는 것을 방지하려면 `=comment` 지시문을 사용하십시오. + +``` +=comment 알고리즘에 대해 여기에 더 추가하십시오. + +=comment Pod 주석은 문서의 문서를 작성하는 데 좋습니다. +``` + +인라인 주석을 만들려면 `Z<>` 코드를 사용하십시오. + +``` +Pod는 훌륭합니다 Z<물론이죠!>. 그리고 Raku도요! +``` + +Raku 인터프리터는 내장된 Pod 블록을 실행하지 않으므로, +주석 블록은 중첩 가능한 블록 주석의 대체 형태로도 사용할 수 있습니다. + +### 링크 + +Pod에서 링크를 만드는 것은 매우 쉽고 `L<>` 코드에 링크를 묶어서 수행됩니다. 일반적인 형식은 `L<레이블|URL>`이며 `레이블`은 선택 사항입니다. + +``` +Raku 홈페이지는 L입니다. +L<클릭하세요!|http://link.org/>. +``` + +상대 경로도 작동합니다. + +``` +L<음악으로 이동|/music/>. +``` + +동일한 문서의 섹션에 연결하는 것도 작동합니다. + +``` +L<제목으로 연결|#Headings> +``` + +### 표 + +Pod 사양은 아직 완전히 제대로 처리되지 않았으며 +여기에는 테이블 처리도 포함됩니다. 간단함을 위해 +테이블을 구성하는 한 가지 방법만 여기에 표시됩니다. 모범 사례를 배우고 +좋은 테이블과 나쁜 테이블의 예시를 보려면 +를 방문하십시오. + +``` +=begin table +옵션 | 설명 +============|================ +data | 데이터 파일 경로. +engine | 템플릿 처리에 사용될 엔진. +ext | 대상 파일에 사용될 확장자. +=end table +``` + +## 블록 구조 + +앞서 언급했듯이, Pod 문서는 지시문을 사용하여 지정되며, 이는 텍스트 콘텐츠 블록을 구분하고 선택적 [구성 정보](#configuration-data)를 선언하는 데 사용됩니다. 모든 지시문은 첫 번째 열에 등호(`=`)로 시작합니다. 문서의 내용은 하나 이상의 블록 내에 지정됩니다. 모든 Pod 블록은 세 가지 동등한 형식 중 하나로 선언될 수 있습니다: 구분된 스타일, 단락 스타일 또는 약어 스타일. + +지금까지는 블록 유형에 대해 약어 스타일만 사용했습니다(예: `=head1`, `=para`, `=comment`, `=item` 등). + +### 약어 블록 + +약어 블록은 첫 번째 열에 `=` 기호로 시작하며, 그 뒤에 블록의 `typename`이 오고 그 뒤에 내용이 옵니다. 줄의 나머지 부분은 블록 데이터로 처리되며 구성으로 처리되지 않습니다. 내용은 다음 Pod 지시문 또는 첫 번째 빈 줄(블록 데이터의 일부가 아님)에서 종료됩니다. 일반적인 구문은 다음과 같습니다. + +``` +=BLOCK_TYPE BLOCK_DATA +``` + +예를 들어: + +``` +=head1 최상위 제목 +``` + +### 구분된 블록 + +구분된 블록은 `=begin` 및 `=end` 마커로 경계가 지정되며, 둘 다 유효한 Pod 식별자(블록의 `typename`)가 뒤따릅니다. 일반적인 구문은 다음과 같습니다. + +``` +=begin BLOCK_TYPE +BLOCK_DATA +=end BLOCK_TYPE +``` + +예를 들어: + +``` +=begin head1 +최상위 제목 +=end head1 +``` + +이러한 유형의 블록은 여러 단락이 있는 제목, 목록 항목, 코드 블록 등을 만드는 데 유용합니다. 예를 들어, + +* 목록의 여러 줄 항목 + +``` +=begin item +이것은 목록 항목의 단락입니다. + +이것은 동일한 목록 항목의 또 다른 단락입니다. +=end item +``` + +* 코드 블록 + +``` +=begin code +#`( +비효율적인 거듭제곱 함수 재귀 구현 (다중 서브 사용). +) + +multi pow( Real $base, 0 ) { 1 } + +multi pow( Real $base, Int $exp where * ≥ 0) { + $base * pow($base, $exp - 1) +} + +multi pow( Real $base ) { + pow($base, 2) +} + +say pow(3, 0); #=> 1 +say pow(4.2, 2); #=> 17.64 +say pow(6); #=> 36 +=end code +``` + +### 단락 블록 + +단락 블록은 `=for` 마커로 시작하며 다음 Pod 지시문 또는 첫 번째 빈 줄(블록 내용의 일부로 간주되지 않음)에서 종료됩니다. `=for` 마커 뒤에는 블록의 `typename`이 옵니다. 일반적인 구문은 다음과 같습니다. + +``` +=for BLOCK_TYPE +BLOCK DATA +``` + +예를 들어: + +``` +=for head1 +최상위 제목 +``` + +## 구성 데이터 + +약어 블록을 제외하고, 구분된 블록과 단락 블록 모두 블록의 `typename` 바로 뒤에 내용에 대한 구성 정보를 제공할 수 있습니다. 따라서 다음은 이러한 블록에 대한 더 일반적인 구문입니다. + +* 구분된 블록 + +``` +=begin BLOCK_TYPE OPTIONAL_CONFIG_INFO += ADDITIONAL_CONFIG_INFO +BLOCK_DATA +=end BLOCK_TYPE +``` + +* 단락 블록 + +``` +=for BLOCK_TYPE OPTIONAL_CONFIG_INFO += ADDITIONAL_CONFIG_INFO +BLOCK DATA +``` + +구성 정보는 Raku의 ["콜론 쌍"](https://docs.raku.org/language/glossary#index-entry-Colon_Pair) 구문과 유사한 형식으로 제공됩니다. 다음 표는 구성 정보를 제공할 수 있는 다양한 방법의 간략화된 버전입니다. 주제에 대한 더 자세한 내용은 을 참조하십시오. + +| 값 | 지정 방법... | 예시 | +| :-------- | :------ | :------ | +| 목록 | :key($elem1, $elem2, ...) | :tags('Pod', 'Raku') | +| 해시 | :key{$key1 => $value1, ...} | :feeds{url => 'raku.org'} | +| 부울 | :key/:key(True) | :skip-test(True) | +| 부울 | :!key/:key(False) | :!skip-test | +| 문자열 | :key('string') | :nonexec-reason('SyntaxError') | +| 정수 | :key(2) | :post-number(6) | + + +### 표준 구성 옵션 + +Pod는 특정 블록 유형에 균일하게 적용할 수 있는 소수의 표준 구성 옵션을 제공합니다. 그 중 일부는 다음과 같습니다. + +* `:numbered` + +이 옵션은 블록에 번호를 매길 것을 지정합니다. 이 옵션의 가장 일반적인 용도는 번호가 매겨진 제목과 순서가 있는 목록을 만드는 것이지만, 어떤 블록에도 적용할 수 있습니다. + +예를 들어: + +``` +=for head1 :numbered +문제 +=for head1 :numbered +해결책 +=for head2 :numbered +분석 +=for head3 :numbered +개요 +``` + +* `:allow` + +`:allow` 옵션의 값은 하나 이상의 서식 코드(단일 문자 이름) 목록이어야 합니다. 그러면 해당 코드는 코드 블록 내에서 활성 상태로 유지됩니다. 이 옵션은 일반적으로 `=code` 블록에서 사용되며, 해당 블록 내에서 마크업을 허용하지만, 다른 모든 블록에서도 사용할 수 있습니다. + +다음 스니펫이 주어졌을 때: + +``` +=begin code :allow('B', 'I') +B greet( $name ) { + B "Hello, $nameI"; +} +=end code +``` + +다음과 같은 출력을 얻습니다: + +
sub greet( $name ) {
+    say "Hello, $name!";
+}
+
+ +이는 출력 형식에 따라 크게 달라집니다. 예를 들어, Pod가 HTML로 변환될 때는 작동하지만, Markdown으로 변환될 때는 보존되지 않을 수 있습니다. + +### 블록 사전 구성 + +`=config` 지시문은 특정 유형의 모든 블록에 적용되는 표준 구성 정보를 미리 지정할 수 있도록 합니다. +구성 지시문의 일반적인 구문은 다음과 같습니다. + +``` +=config BLOCK_TYPE CONFIG OPTIONS += ADDITIONAL_CONFIG_INFO +``` + +예를 들어, 모든 헤더 레벨 1에 번호가 매겨지고 굵게 및 밑줄이 그어지도록 지정하려면 `=head1`을 다음과 같이 미리 구성합니다. + +``` +=config head1 :formatted('B', 'U') :numbered +``` + +## 의미 블록 + +모든 대문자 블록 유형 이름은 표준 문서, 게시, 소스 구성 요소 또는 메타 정보를 지정하는 데 예약되어 있습니다. +그 중 일부는 다음과 같습니다. + +``` +=NAME +=AUTHOR +=VERSION +=CREATED +=SYNOPSIS +=DESCRIPTION +=USAGE +``` + +이러한 블록 대부분은 일반적으로 전체 구분된 형식으로 사용됩니다. 예를 들어, + +``` +=NAME B + +=begin DESCRIPTION +이 모듈은 문서를 자동으로 생성하는 데 도움이 됩니다. +소스 코드가 필요 없습니다! 대부분은 블랙홀에서 가져왔습니다. +=end DESCRIPTION + +=begin SYNOPSIS +=begin code + use Doc::Magic; + + my Doc::Magic $doc .= new(); + + my $result = $doc.create-documentation($fh); +=end code +=end SYNOPSIS + +=AUTHOR Authorius Docus +=VERSION 42 +``` + +## 기타 + +### 참고 + +참고는 각주로 렌더링되며 `N<>` 코드에 참고를 묶어서 생성됩니다. + +``` +또한, 이 언어는 다중 패러다임입니다 N<위키백과에 따르면, +이것은 절차적, 객체 지향적, 함수형 프로그래밍을 지원한다는 의미입니다.> +``` + +### 키보드 입력 + +텍스트를 키보드 입력으로 표시하려면 `K<>` 코드로 묶으십시오. + +``` +이름을 입력하십시오 K +``` + +### 터미널 출력 + +터미널 출력으로 텍스트를 표시하려면 `T<>` 코드로 묶으십시오. + +``` +안녕하세요, T +``` + +### 유니코드 + +Pod 문서에 유니코드 코드 포인트 또는 HTML5 문자 참조를 포함하려면 `E<>` 코드로 묶으십시오. + +예를 들어: + +``` +Raku는 E<171> 및 E<187> 문자를 상당히 사용합니다. +Raku는 E 및 E 문자를 상당히 사용합니다. +``` + +다음과 같이 렌더링됩니다: + +Raku는 « 및 » 문자를 상당히 사용합니다. +Raku는 « 및 » 문자를 상당히 사용합니다. + +## Pod 렌더링 + +출력(예: Markdown, HTML, Text 등)을 생성하려면 Rakudo Raku 컴파일러가 설치되어 있어야 합니다. 또한 Pod에서 원하는 출력을 생성하는 모듈(예: `Pod::To::Markdown`, `Pod::To::HTML`, `Pod::To::Text` 등)을 설치해야 합니다. + +raku 프로그램을 실행하기 위해 Rakudo를 설치하는 방법에 대한 지침은 [여기](https://raku.org/downloads/)를 참조하십시오. + +특정 출력을 생성하려면 다음 명령을 실행하십시오. + +``` +raku --doc=TARGET input.pod6 > output.html +``` + +여기서 `TARGET`은 `Markdown`, `HTML`, `Text` 등입니다. 따라서 Pod에서 Markdown을 생성하려면 다음을 실행하십시오. + +``` +raku --doc=Markdown input.pod6 > output.html +``` + +## Pod 액세스 + +Raku 프로그램 내에서 Pod 문서에 액세스하려면 특수 `=` 트위길(예: `$=pod`, `$=SYNOPSIS` 등)을 사용해야 합니다. + +`$=` 구문은 Pod 구조에 대한 인트로스펙션을 제공하여 `Pod::Block` 트리 루트를 생성하며, 이를 통해 Pod 문서의 전체 구조에 액세스할 수 있습니다. + +다음 Raku 코드와 [의미 블록](#semantic-blocks) 섹션의 Pod 문서를 동일한 파일에 배치하면: + +``` +my %used-directives; +for $=pod -> $pod-item { + for $pod-item.contents -> $pod-block { + next unless $pod-block ~~ Pod::Block::Named; + %used-directives{$pod-block.name} = True; + } +} + +say %used-directives.keys.join("\n"); +``` + +다음과 같은 출력을 얻습니다: + +``` +SYNOPSIS +NAME +VERSION +AUTHOR +DESCRIPTION +``` + +## 추가 정보 + +* Pod 문서. +* Pod 테이블에 대한 조언. +* Pod 사양. + +``` \ No newline at end of file diff --git a/ko/raku.md b/ko/raku.md new file mode 100644 index 0000000000..dff84f757c --- /dev/null +++ b/ko/raku.md @@ -0,0 +1,579 @@ +--- +name: Raku +filename: learnraku.raku +contributors: + - ["vendethiel", "http://github.com/vendethiel"] + - ["Samantha McVey", "https://cry.nu"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Raku (이전 Perl 6)는 최소한 향후 100년을 위해 만들어진 매우 유능하고 기능이 풍부한 프로그래밍 언어입니다. + +주요 Raku 컴파일러는 [Rakudo](http://rakudo.org)라고 불리며, JVM 및 [MoarVM](http://moarvm.com)에서 실행됩니다. + +메타 노트: + +* 파운드 기호(`#`)는 문장과 노트에 사용되지만, Pod 스타일 주석(아래에서 자세히 설명)은 편리할 때마다 사용됩니다. +* `# OUTPUT:`은 모든 표준 스트림에 대한 명령의 출력을 나타내는 데 사용됩니다. 출력에 줄 바꿈이 있으면 `␤` 기호로 표시됩니다. 출력은 항상 꺾쇠 괄호(`«` 및 `»`)로 묶입니다. +* `#=>`는 표현식의 값, 서브루틴의 반환 값 등을 나타냅니다. 경우에 따라 값은 주석과 함께 제공됩니다. +* 백틱은 언어 구문을 텍스트와 구별하고 강조하는 데 사용됩니다. + +```perl6 +#################################################### +# 0. 주석 +#################################################### + +# 한 줄 주석은 파운드 기호로 시작합니다. + +#`( 여러 줄 주석은 #`와 인용 구문을 사용합니다. + (), [], {}, 「」 등은 작동합니다. +) + +=for comment +주석을 포함하기 위해 여러 줄 주석과 동일한 구문을 사용하십시오. +for #`(각 요소에 대해) @array { + put #`(또는 요소 인쇄) $_ #`(줄 바꿈 포함); +} + +# Pod 스타일 주석도 사용할 수 있습니다. 예를 들어: + +=comment 알고리즘에 대해 여기에 더 추가하십시오. + +=comment Pod 주석은 문서의 문서를 작성하는 데 좋습니다. + +=begin comment +이 주석은 여러 줄입니다. + +빈 줄도 여기에 있을 수 있습니다! +=end comment + +#################################################### +# 1. 변수 +#################################################### + +# Raku에서 `my` 키워드를 사용하여 어휘 변수를 선언합니다: +my $variable; + +# Raku에는 스칼라, 배열, 해시의 세 가지 기본 변수 유형이 있습니다. + +# +# 1.1 스칼라 +# + +# 스칼라는 단일 값을 나타냅니다. `$` 시길로 시작합니다: +my $str = 'String'; + +# 큰따옴표는 보간을 허용합니다(나중에 설명): +my $str2 = "$str"; + +# 변수 이름은 작은따옴표와 대시를 포함할 수 있지만 끝날 수는 없으며, +# 밑줄을 포함할 수 있습니다(그리고 끝날 수 있습니다): +my $person's-belongings = 'towel'; # 이것은 작동합니다! + +my $bool = True; # `True`와 `False`는 Raku의 부울 값입니다. +my $inverse = !$bool; # 접두사 `!` 연산자로 부울을 반전합니다. +my $forced-bool = so $str; # 그리고 접두사 `so` 연산자를 사용할 수 있습니다. +$forced-bool = ?$str; # 또는 `?`를 사용하여 피연산자를 부울로 변환합니다. + +# +# 1.2 배열 및 목록 +# + +# 배열은 여러 값을 나타냅니다. 배열 변수는 `@` 시길로 시작합니다. 목록과 달리 배열은 변경 가능합니다. + +my @array = 'a', 'b', 'c'; +# 다음과 동일합니다: +my @letters = ; +# 이전 문에서 공백으로 구분된 단어 배열에 대해 quote-words(`<>`) 용어를 사용합니다. +# Perl의 qw 또는 Ruby의 %w와 유사합니다. + +@array = 1, 2, 4; + +# 배열 인덱스는 0부터 시작합니다. 여기서는 세 번째 요소에 액세스합니다. +say @array[2]; # OUTPUT: «4␤» + +say "배열을 보간하려면 []를 사용하십시오: @array[]"; +# OUTPUT: «배열을 보간하려면 []: 1 2 3␤» + +@array[0] = -1; # 배열 인덱스에 새 값 할당 +@array[0, 1] = 5, 6; # 여러 값 할당 + +my @keys = 0, 2; +@array[@keys] = @letters; # 인덱스 값을 포함하는 배열을 사용하여 할당 +say @array; # OUTPUT: «a 6 b␤» + +# +# 1.3 해시 또는 키-값 쌍. +# + +# 해시는 키와 값 쌍의 집합입니다. `key => value` 구문을 사용하여 `Pair` 객체를 구성할 수 있습니다. +# 해시 테이블은 조회 속도가 매우 빠르며, 정렬되지 않은 상태로 저장됩니다. 키는 해시 컨텍스트에서 "평탄화"되고 중복된 키는 중복 제거됩니다. +my %hash = 'a' => 1, 'b' => 2; + +# fat comma(`=>`)를 사용하면 키가 자동으로 인용됩니다. 후행 쉼표는 괜찮습니다. +%hash = a => 1, b => 2, ; + +# 해시가 배열과 다르게 내부적으로 저장되더라도, +# Raku는 짝수 배열에서 해시를 쉽게 만들 수 있도록 합니다: +my %hash = ; # 또는: +my %hash = "key1", "value1", "key2", "value2"; + +%hash = key1 => 'value1', key2 => 'value2'; # 위와 동일한 결과 + +# "콜론 쌍" 구문도 사용할 수 있습니다. 이 구문은 특히 나중에 설명할 명명된 매개변수에 유용합니다. +%hash = :n(2), # `n => 2`와 동일 + :is-even, # `:is-even(True)` 또는 `is-even => True`와 동일 + :!is-odd, # `:is-odd(False)` 또는 `is-odd => False`와 동일 +; +# `:`(예: `:is-even`) 및 `:!`(예: `:!is-odd`) 구문은 각각 `True` 및 `False` 단축키로 알려져 있습니다. + +# 아래 예제에서 보여주듯이, {}를 사용하여 키에서 값을 가져올 수 있습니다. +# 공백이 없는 문자열인 경우 실제로 quote-words 연산자(`<>`)를 사용할 수 있습니다. +# Perl과 달리 Raku는 bareword를 사용하지 않으므로 `{key1}`은 작동하지 않습니다. +say %hash{'n'}; # OUTPUT: «2␤», 키 'n'과 연결된 값을 가져옵니다. +say %hash; # OUTPUT: «True␤», 키 'is-even'과 연결된 값을 가져옵니다. + +#################################################### +# 2. 서브루틴 +#################################################### + +# 서브루틴 또는 대부분의 다른 언어에서 함수라고 부르는 것은 `sub` 키워드로 생성됩니다. +sub say-hello { say "Hello, world" } + +# (타입이 지정된) 인수를 제공할 수 있습니다. 지정된 경우, 타입은 컴파일 타임에 확인됩니다(가능한 경우). 그렇지 않으면 런타임에 확인됩니다. +sub say-hello-to( Str $name ) { + say "Hello, $name !"; +} + +# 서브루틴은 블록의 마지막 값을 반환합니다. 마찬가지로 마지막 표현식의 세미콜론은 생략할 수 있습니다. +sub return-value { 5 } +say return-value; # OUTPUT: «5␤» + +sub return-empty { } +say return-empty; # OUTPUT: «Nil␤» + +# 일부 제어 흐름 구조는 값을 생성합니다. 예를 들어 `if`: +sub return-if { + if True { "Truthy" } +} +say return-if; # OUTPUT: «Truthy␤» + +# `for`와 같이 일부는 그렇지 않습니다: +sub return-for { + for 1, 2, 3 { 'Hi' } +} +say return-for; # OUTPUT: «Nil␤» + +# 위치 인수는 기본적으로 필수입니다. 선택 사항으로 만들려면 매개변수 이름 뒤에 `?`를 사용하십시오. + +# 다음 예제에서 `with-optional` 서브루틴은 인수가 전달되지 않으면 `(Any)`(Perl의 null과 유사한 값)를 반환합니다. 그렇지 않으면 인수를 반환합니다. +sub with-optional( $arg? ) { + $arg; +} +with-optional; # Any 반환 +with-optional(); # Any 반환 +with-optional(1); # 1 반환 + +# 또한 전달되지 않을 때 기본값을 제공할 수 있습니다. 이렇게 하면 해당 매개변수가 선택 사항이 됩니다. 필수 매개변수는 선택 사항 매개변수보다 먼저 와야 합니다. + +# `greeting` 서브루틴에서 `$type` 매개변수는 선택 사항입니다. +sub greeting( $name, $type = "Hello" ) { + say "$type, $name!"; +} + +greeting("Althea"); # OUTPUT: «Hello, Althea!␤» +greeting("Arthur", "Good morning"); # OUTPUT: «Good morning, Arthur!␤» + +# 해시와 유사한 구문(통합 구문!)을 사용하여 명명된 매개변수를 선언하고 서브루틴에 명명된 인수를 전달할 수도 있습니다. +# 기본적으로 명명된 매개변수는 선택 사항이며 `Any`로 기본 설정됩니다. +sub with-named( $normal-arg, :$named ) { + say $normal-arg + $named; +} +with-named(1, named => 6); # OUTPUT: «7␤» + +# 여기에 주의해야 할 한 가지 함정이 있습니다: 키를 인용하면 Raku는 컴파일 타임에 키를 볼 수 없으며, 단일 `Pair` 객체가 위치 매개변수로 사용됩니다. 이는 함수 서브루틴 `with-named(1, 'named' => 6);`이 실패함을 의미합니다. +with-named(2, :named(5)); # OUTPUT: «7␤» + +# 명명된 매개변수를 강제하려면 매개변수에 `!`를 추가할 수 있습니다. +# 이는 필수 매개변수를 선택 사항으로 만드는 `?`의 반대입니다. + +sub with-mandatory-named( :$str! ) { + say "$str!"; +} +with-mandatory-named(str => "My String"); # OUTPUT: «My String!␤» +# with-mandatory-named; # 런타임 오류: "필수 명명된 매개변수가 전달되지 않았습니다." +# with-mandatory-named(3);# 런타임 오류: "너무 많은 위치 매개변수가 전달되었습니다." + +# 서브루틴이 명명된 부울 인수를 취하는 경우, 이전에 논의한 것과 동일한 "짧은 부울" 해시 구문을 사용할 수 있습니다. +sub takes-a-bool( $name, :$bool ) { + say "$name takes $bool"; +} +takes-a-bool('config', :bool); # OUTPUT: «config takes True␤» +takes-a-bool('config', :!bool); # OUTPUT: «config takes False␤» + +# 서브루틴을 호출할 때 괄호를 생략할 수 있으므로, +# 인수가 없는 서브루틴 호출과 코드 객체를 구별하려면 `&`를 사용해야 합니다. + +# 예를 들어, 이 예제에서는 `&`를 사용하여 `say-hello` 서브루틴(즉, 서브루틴의 코드 객체)을 변수에 저장해야 합니다. 서브루틴 호출이 아닙니다. +my &s = &say-hello; +my &other-s = sub { say "Anonymous function!" } + +# 서브루틴은 "slurpy" 매개변수를 가질 수 있습니다. 이는 "얼마나 많은지 상관없는" 매개변수라고 할 수 있습니다. +# 이를 위해 `*@`(slurpy)를 사용해야 합니다. 이는 "나머지 모든 것을 가져옵니다". +# slurpy 매개변수 *앞에는* 원하는 만큼의 매개변수를 가질 수 있지만, *뒤에는* 가질 수 없습니다. +sub as-many($head, *@rest) { + @rest.join(' / ') ~ " !"; +} +say as-many('Happy', 'Happy', 'Birthday'); # OUTPUT: «Happy / Birthday !␤» +say as-many('Happy', ['Happy', 'Birthday'], 'Day'); # OUTPUT: «Happy / Birthday / Day !␤» + +# 스플랫(*)은 그 앞에 있는 매개변수를 소비하지 않았다는 점에 유의하십시오. + +# Raku에는 두 가지 다른 slurpy 매개변수 변형이 있습니다. 이전 것(즉, `*@`), 평탄화된 slurpy로 알려져 있으며, 전달된 인수를 평탄화합니다. 다른 두 가지는 `**@` 및 `+@`로, 각각 평탄화되지 않은 slurpy 및 "단일 인수 규칙" slurpy로 알려져 있습니다. 평탄화되지 않은 slurpy는 목록 인수를 평탄화하지 않습니다(또는 Iterable 인수). +sub b(**@arr) { @arr.perl.say }; +b(['a', 'b', 'c']); # OUTPUT: «[["a", "b", "c"],]» +b(1, $('d', 'e', 'f'), [2, 3]); # OUTPUT: «[1, ("d", "e", "f"), [2, 3]]» +b(1, [1, 2], ([3, 4], 5)); # OUTPUT: «[1, [1, 2], ([3, 4], 5)]␤» + +# 반면에 "단일 인수 규칙" slurpy는 "단일 인수 규칙"을 따릅니다. +# 이는 컨텍스트에 따라 slurpy 인수를 처리하는 방법을 지시하며, +# 단일 인수만 전달되고 해당 인수가 Iterable인 경우 +# 해당 인수가 slurpy 매개변수 배열을 채우는 데 사용된다는 것을 대략적으로 명시합니다. +# 다른 모든 경우에 `+@`는 `**@`처럼 작동합니다. +sub c(+@arr) { @arr.perl.say }; +c(['a', 'b', 'c']); # OUTPUT: «["a", "b", "c"]␤» +c(1, $('d', 'e', 'f'), [2, 3]); # OUTPUT: «[1, ("d", "e", "f"), [2, 3]]␤» +c(1, [1, 2], ([3, 4], 5)); # OUTPUT: «[1, [1, 2], ([3, 4], 5)]␤» + +# `|` (이 연산자의 유일한 역할은 아니지만)를 사용하여 배열로 함수를 호출할 수 있습니다. +sub concat3($a, $b, $c) { + say "$a, $b, $c"; +} +concat3(|@array); # OUTPUT: «a, b, c␤» + # `@array`가 인수 목록의 일부로 "평탄화"되었습니다. + +#################################################### +# 3. 컨테이너 +#################################################### + +# Raku에서 값은 실제로 "컨테이너"에 저장됩니다. 할당 연산자는 왼쪽에 있는 컨테이너에게 오른쪽에 있는 값을 저장하도록 요청합니다. +# 전달될 때 컨테이너는 불변으로 표시됩니다. 즉, 함수에서 인수를 변경하려고 하면 오류가 발생합니다. +# 정말로 필요한 경우 `is rw` 특성을 사용하여 변경 가능한 컨테이너를 요청할 수 있습니다. +sub mutate( $n is rw ) { + $n++; # 후위 ++ 연산자는 인수를 증가시키지만 이전 값을 반환합니다. +} +my $m = 42; +mutate $m; #=> 42, 값이 증가했지만 이전 값이 반환됩니다. +say $m; # OUTPUT: «43␤» + +# 컨테이너 $m을 `mutate` 서브루틴에 전달하고 있기 때문에 작동합니다. +# 변수가 아닌 숫자를 전달하려고 하면 컨테이너가 전달되지 않고 정수 자체는 불변이므로 작동하지 않습니다: + +# mutate 42; # 매개변수 '$n'은 쓰기 가능한 컨테이너를 예상했지만 Int 값을 받았습니다. + +# 마찬가지로, 바인딩된 변수가 서브루틴에 전달되면 유사한 오류가 발생합니다. Raku에서 값을 변수에 바인딩하려면 바인딩 연산자 `:=`를 사용합니다. +my $v := 50; # 변수 $v에 50 바인딩 +# mutate $v; # 매개변수 '$n'은 쓰기 가능한 컨테이너를 예상했지만 Int 값을 받았습니다. + +# 대신 복사본을 원한다면 `is copy` 특성을 사용하여 인수를 복사하고 전달된 인수를 수정하지 않고 서브루틴 내에서 인수를 수정할 수 있습니다. + +# 서브루틴 자체는 컨테이너를 반환합니다. 즉, `rw`로 표시할 수 있습니다. +# 또는 `return` 대신 `return-rw`를 사용하여 반환된 컨테이너를 명시적으로 변경 가능하게 표시할 수 있습니다. +my $x = 42; +my $y = 45; +sub x-store is rw { $x } +sub y-store { return-rw $y } + +# 이 경우 괄호는 필수입니다. 그렇지 않으면 Raku는 `x-store`와 `y-store`를 식별자로 간주합니다. +x-store() = 52; +y-store() *= 2; + +say $x; # OUTPUT: «52␤» +say $y; # OUTPUT: «90␤» + +#################################################### +# 4. 제어 흐름 구조 +#################################################### + +# +# 4.1 if/if-else/if-elsif-else/unless +# + +# `if`에 대해 이야기하기 전에 어떤 값이 "참"(`True`를 나타냄)이고 어떤 값이 "거짓"(`False`를 나타냄)인지 알아야 합니다. 0, (), {}, "", Nil, 유형(예: `Str`, `Int` 등) 및 물론 `False` 자체만 거짓입니다. 다른 모든 값은 참입니다. +my $number = 5; +if $number < 5 { + say "Number is less than 5" +} +elsif $number == 5 { + say "Number is equal to 5" +} +else { + say "Number is greater than 5" +} + +unless False { + say "It's not false!"; +} + +# `unless`는 조건문의 의미를 반전시키는 `if not (X)`와 유사합니다. 그러나 `else` 또는 `elsif`와 함께 사용할 수 없습니다. + +# 문 수정자(postfix) 버전도 사용할 수 있습니다: +say "Quite truthy" if True; # OUTPUT: «Quite truthy␤» +say "Quite falsey" unless False; # OUTPUT: «Quite falsey␤» + +# 삼항 연산자(`??..!!`)는 `condition ?? expression1 !! expression2`와 같이 구성되며, 조건이 참이면 expression1을 반환합니다. 그렇지 않으면 expression2를 반환합니다. +my $age = 30; +say $age > 18 ?? "You are an adult" !! "You are under 18"; +# OUTPUT: «You are an adult␤» + +# +# 4.2 with/with-else/with-orwith-else/without +# + +# `with` 문은 `if`와 유사하지만, 참 여부 대신 정의 여부를 테스트하며, 나중에 논의될 `given`과 마찬가지로 조건에 따라 주제를 정합니다. +my $s = "raku"; +with $s.index("r") { say "Found a at $_" } +orwith $s.index("k") { say "Found c at $_" } +else { say "Didn't find r or k" } + +# 거짓 여부를 확인하는 `unless`와 유사하게, 정의되지 않은 여부를 확인하는 `without`을 사용할 수 있습니다. +my $input01; +without $input01 { + say "No input given." +} +# OUTPUT: «No input given.␤» + +# `with`와 `without` 모두에 대한 문 수정자 버전도 있습니다. +say $input02 with $input02; # OUTPUT: «Hello␤» +say "No input given." without $input02; + +# +# 4.3 given/when, 또는 Raku의 switch 구문 +# + +=begin comment +`given...when`은 다른 언어의 `switch`와 유사하지만, 스마트 매칭과 Raku의 "토픽 변수" `$_` 덕분에 훨씬 더 강력합니다. + +토픽 변수 `$_`는 블록의 기본 인수, 루프의 현재 반복(명시적으로 명명되지 않은 경우) 등을 포함합니다. + +`given`은 단순히 인수를 `$_`에 넣고(블록처럼), `when`은 "스마트 매칭"(`~~`) 연산자를 사용하여 비교합니다. + +다른 Raku 구성도 이 변수를 사용하므로(`for`, 블록, `with` 문 등), 강력한 `when`은 `given`과 함께 사용할 수 있을 뿐만 아니라 `$_`가 존재하는 모든 곳에서 사용할 수 있습니다. + +=end comment + +given "foo bar" { + say $_; # OUTPUT: «foo bar␤» + + # 스마트 매칭에 대해 아직 걱정하지 마십시오. `when`이 스마트 매칭을 사용한다는 것만 아십시오. 이것은 `if $_ ~~ /foo/`와 동일합니다. + when /foo/ { + say "Yay !"; + } + + # `True`와 스마트 매칭하는 것은 항상 `True`입니다. 즉, (`$a ~~ True`) + # 따라서 "일반적인" 조건문을 넣을 수도 있습니다. 예를 들어, 이 `when`은 + # 이 `if`와 동일합니다: `if $_ ~~ ($_.chars > 50) {...}` + # 즉: `if $_.chars > 50 {...}` + when $_.chars > 50 { + say "Quite a long string !"; + } + + # `when *`와 동일합니다(Whatever Star 사용). + default { + say "Something else" + } +} + +# +# 4.4 루프 구성 +# + +# `loop` 구성은 인수를 전달하지 않으면 무한 루프이지만, +# C 스타일 `for` 루프일 수도 있습니다: +loop { + say "This is an infinite loop !"; + last; +} +# 이전 예제에서 `last`는 다른 언어의 `break` 키워드와 매우 유사하게 루프를 빠져나옵니다. + +# `next` 키워드는 다른 언어의 `continue`와 유사하게 다음 반복으로 건너뜁니다. +# 또한 postfix 조건문, 루프 등을 사용할 수 있습니다. +loop (my $i = 0; $i < 5; $i++) { + next if $i == 3; + say "This is a C-style for loop!"; +} + +# `for` 구성은 요소 목록을 반복합니다. +my @odd-array = 1, 3, 5, 7, 9; + +# 토픽 변수 $_를 사용하여 배열의 요소에 액세스합니다. +for @odd-array { + say "I've got $_ !"; +} + +# "pointy block"(`->`)을 사용하여 배열의 요소에 액세스합니다. +# 여기서 각 요소는 읽기 전용입니다. +for @odd-array -> $variable { + say "I've got $variable !"; +} + +# "doubly pointy block"(`<->`)을 사용하여 배열의 요소에 액세스합니다. +# 여기서 각 요소는 읽기-쓰기이므로 `$variable`을 변경하면 +# 배열의 해당 요소가 변경됩니다. +for @odd-array <-> $variable { + say "I've got $variable !"; +} + +# `given`에서 본 것처럼, `for` 루프의 기본 "현재 반복" 변수는 `$_`입니다. +# 즉, `given`에서와 마찬가지로 `for` 루프에서도 `when`을 사용할 수 있습니다. +for @odd-array { + say "I've got $_"; + + # 이것도 허용됩니다. "topic"(수신자)이 없는 점 호출은 기본적으로 `$_`(토픽 변수)로 전송됩니다. + .say; + + # 위 문과 동일합니다. + $_.say; +} + +for @odd-array { + # 다음을 수행할 수 있습니다... + next if $_ == 3; # 다음 반복으로 건너뛰기 (C-like 언어의 `continue`) + redo if $_ == 4; # 동일한 토픽 변수(`$_`)를 유지하면서 반복 다시 실행 + last if $_ == 5; # 또는 루프에서 벗어나기 (C-like 언어의 `break`) +} + +# "pointy block" 구문은 `for` 루프에만 국한되지 않습니다. Raku에서 블록을 표현하는 방법일 뿐입니다. +sub long-computation { "Finding factors of large primes" } +if long-computation() -> $result { + say "The result is $result."; +} + +#################################################### +# 5. 연산자 +#################################################### + +=begin comment +Perl 언어는 연산자 기반 언어이므로 Raku 연산자는 실제로 +`infix:<+>`(덧셈) 또는 `prefix:`(부울 부정)와 같은 구문 범주에서 +재미있는 모양의 서브루틴일 뿐입니다. + +범주는 다음과 같습니다: + - "prefix": 앞에 (예: `!True`의 `!`). + - "postfix": 뒤에 (예: `$a++`의 `++`). + - "infix": 사이에 (예: `4 * 3`의 `*`). + - "circumfix": 주위에 (예: `[1, 2]`의 `[`-`]`). + - "post-circumfix": 다른 용어 뒤에 주위에 (예: `%hash{'key'}`의 `{`-`}`) + +결합성 및 우선 순위 목록은 아래에 설명되어 있습니다. + +자, 이제 시작할 준비가 되었습니다! + +=end comment + +# +# 5.1 같음 확인 +# + +# `==`는 숫자 비교입니다. +say 3 == 4; # OUTPUT: «False␤» +say 3 != 4; # OUTPUT: «True␤» + +# `eq`는 문자열 비교입니다. +say 'a' eq 'b'; # OUTPUT: «False␤» +say 'a' ne 'b'; # OUTPUT: «True␤», 같지 않음 +say 'a' !eq 'b'; # OUTPUT: «True␤», 위와 동일 + +# `eqv`는 정규 동등성(또는 "깊은 동등성")입니다. +say (1, 2) eqv (1, 3); # OUTPUT: «False␤» +say (1, 2) eqv (1, 2); # OUTPUT: «True␤» +say Int === Int; # OUTPUT: «True␤» + +# `~~`는 왼쪽을 `$_`에 별칭으로 지정한 다음 오른쪽을 평가하는 스마트 일치 연산자입니다. +# 여기서는 `Match` 객체를 반환하며, 정규식이 일치하면 `True`로 평가됩니다. +# 일치 결과는 `$/` 변수(암시적으로 어휘적으로 범위 지정됨)에서 사용할 수 있습니다. 또한 0부터 시작하는 캡처 변수 `$0`, `$1`, `$2` 등을 사용할 수 있습니다. + +# `~~`는 시작/끝 검사를 수행하지 않습니다. 즉, 정규식은 문자열의 한 문자만으로 일치할 수 있습니다. 나중에 이 작업을 수행하는 방법을 설명합니다. + +# Raku에서는 모든 영숫자를 리터럴로 사용할 수 있으며, 다른 모든 것은 백슬래시나 따옴표를 사용하여 이스케이프해야 합니다. +say so 'a|b' ~~ / a '|' b /; # OUTPUT: «True␤», `|`가 이스케이프되지 않으면 동일한 의미가 아닙니다. +say so 'a|b' ~~ / a \| b /; # OUTPUT: «True␤», 이스케이프하는 또 다른 방법입니다. + +# 정규식의 공백은 중요하지 않습니다. `:s`(`:sigspace`, 중요한 공백) 부사를 사용하지 않는 한. +say so 'a b c' ~~ / a b c /; #=> `False`, 여기서 공백은 중요하지 않습니다! +say so 'a b c' ~~ /:s a b c /; #=> `True`, 여기에 `:s` 수정자를 추가했습니다. + +# 정규식에서 문자열 사이에 공백을 하나만 사용하면 공백이 정규식에서 중요하지 않다는 경고가 표시됩니다: +say so 'a b c' ~~ / a b c /; # OUTPUT: «False␤» +say so 'a b c' ~~ / a b c /; # OUTPUT: «False» + +# 참고: 따옴표나 `:s`(`:sigspace`) 수정자를 사용하십시오(또는 이 경고를 억제하려면 공백을 생략하거나 다른 방식으로 간격을 변경하십시오). 이 문제를 해결하고 공백을 덜 모호하게 만들려면 문자열 사이에 최소 두 개의 공백을 사용하거나 `:s` 부사를 사용하십시오. + +# 이전에 본 것처럼, `:s`를 슬래시 구분 기호 안에 포함할 수 있지만, `m`을 '일치'로 지정하면 슬래시 구분 기호 외부에 배치할 수도 있습니다: +say so 'abc' ~~ m:s/a b c/; # OUTPUT: «True␤» + +# `m/.../`는 `/.../`와 동일합니다: +say 'raku' ~~ m/raku/; # OUTPUT: «True␤» +say 'raku' ~~ /raku/; # OUTPUT: «True␤» + +# `:i` 부사를 사용하여 대소문자 구분을 지정합니다: +say so 'ABC' ~~ m:i{a b c}; # OUTPUT: «True␤» + +# 그러나 공백은 수정자가 적용되는 방식에 중요합니다. +# (아래에서 설명) + +# +# 5.1 수량자 - `?`, `+`, `*` 및 `**`. +# + +# `?` - 0개 또는 1개 일치 +say so 'ac' ~~ / a b c /; # OUTPUT: «False␤» +say so 'ac' ~~ / a b? c /; # OUTPUT: «True␤», "b"가 0번 일치했습니다. +say so 'abc' ~~ / a b? c /; # OUTPUT: «True␤», "b"가 1번 일치했습니다. + +# ... 이전에 읽었듯이, 공백은 수정자가 적용되는 정규식의 대상을 결정하므로 중요합니다: +say so 'def' ~~ / a b c? /; # OUTPUT: «False␤», "c"만 선택 사항입니다. +say so 'def' ~~ / a b? c /; # OUTPUT: «False␤», 공백은 중요하지 않습니다. +say so 'def' ~~ / 'abc'? /; # OUTPUT: «True␤», 전체 "abc" 그룹은 선택 사항입니다. + +# 여기서(그리고 아래에서) 수량자는 "b"에만 적용됩니다. + +# `+` - 1개 이상 일치 +say so 'ac' ~~ / a b+ c /; # OUTPUT: «False␤», `+`는 최소한 하나의 'b'를 원합니다. +say so 'abc' ~~ / a b+ c /; # OUTPUT: «True␤», 하나면 충분합니다. +say so 'abbbbc' ~~ / a b+ c /; # OUTPUT: «True␤», 4개의 "b"가 일치했습니다. + +# `*` - 0개 이상 일치 +say so 'ac' ~~ / a b* c /; # OUTPUT: «True␤», 모두 선택 사항입니다. +say so 'abc' ~~ / a b* c /; # OUTPUT: «True␤» +say so 'abbbbc' ~~ / a b* c /; # OUTPUT: «True␤» +say so 'aec' ~~ / a b* c /; # OUTPUT: «False␤», "b"는 선택 사항이지만 대체할 수 없습니다. + +# `**` - (무한) 수량자 +# 열심히 노력하면 왜 거듭제곱이 수량에 사용되는지 이해할 수 있을 것입니다. +say so 'abc' ~~ / a b**1 c /; # OUTPUT: «True␤», 정확히 한 번 +say so 'abc' ~~ / a b**1..3 c /; # OUTPUT: «True␤», 1에서 3번 +say so 'abbbc' ~~ / a b**1..3 c /; # OUTPUT: «True␤» +say so 'abbbbbbc' ~~ / a b**1..3 c /; # OUTPUT: «Fals␤», 너무 많음 +say so 'abbbbbbc' ~~ / a b**3..* c /; # OUTPUT: «True␤», 무한 범위는 괜찮습니다. + +# +# 18.2 `<[]>` - 문자 클래스 +# + +# 문자 클래스는 PCRE의 `[]` 클래스와 동일하지만, 더 Raku스러운 구문을 사용합니다: +say 'fooa' ~~ / f <[ o a ]>+ /; # OUTPUT: «fooa␤» + +# 범위(`..`)를 사용할 수 있습니다: +say 'aeiou' ~~ / a <[ e..w ]> /; # OUTPUT: «ae␤» + +# 일반 정규식과 마찬가지로 특수 문자를 사용하려면 백슬래시나 따옴표를 사용하여 이스케이프해야 합니다(마지막은 공백을 이스케이프하는 것으로, ' '를 사용하는 것과 동일합니다): +say 'he-he !' ~~ / 'he-' <[ a..z \! \ ]> + /; # OUTPUT: «he-he !␤» + +# 중복된 이름을 넣으면 경고가 표시됩니다(원시 인용을 잡는 좋은 효과가 있습니다): +'he he' ~~ / <[ h e ' ' ]> /; +# "문자 클래스에서 예기치 않게 반복된 문자(')가 발견되었습니다" 경고 + +# 문자 클래스를 부정할 수도 있습니다... (`<-[]>`는 PCRE의 `[^]`와 동일합니다) +say so 'foo' ~~ / <-[ f o ]> + /; # OUTPUT: «False␤» \ No newline at end of file diff --git a/ko/raylib.md b/ko/raylib.md new file mode 100644 index 0000000000..958bc6d4e8 --- /dev/null +++ b/ko/raylib.md @@ -0,0 +1,142 @@ +--- +category: framework +name: raylib +filename: learnraylib.c +contributors: + - ["Nikolas Wipper", "https://notnik.cc"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +**raylib**은 OpenGL 1.1, 2.1, 3.3 및 OpenGL ES 2.0을 기반으로 구축된 크로스 플랫폼 사용하기 쉬운 그래픽 라이브러리입니다. C로 작성되었지만 50개 이상의 다른 언어에 바인딩되어 있습니다. 이 튜토리얼에서는 C, 특히 C99를 사용합니다. + +```c +#include + +int main(void) +{ + const int screenWidth = 800; + const int screenHeight = 450; + + // raylib을 초기화하기 전에 구성 플래그를 설정할 수 있습니다. + SetConfigFlags(FLAG_MSAA_4X_HINT | FLAG_VSYNC_HINT); + + // raylib은 인스턴스 구조체를 저장할 필요가 없습니다. + // 현재 raylib은 한 번에 하나의 창만 처리할 수 있습니다. + InitWindow(screenWidth, screenHeight, "MyWindow"); + + // 게임을 초당 60프레임으로 실행하도록 설정합니다. + SetTargetFPS(60); + + // 창을 닫는 키를 설정합니다. + // 키가 없으면 0이 될 수 있습니다. + SetExitKey(KEY_DELETE); + + // raylib은 Camera3D와 Camera2D의 두 가지 유형의 카메라를 정의합니다. + // Camera는 Camera3D의 typedef입니다. + Camera camera = { + .position = {0.0f, 0.0f, 0.0f}, + .target = {0.0f, 0.0f, 1.0f}, + .up = {0.0f, 1.0f, 0.0f}, + .fovy = 70.0f, + .projection = CAMERA_PERSPECTIVE + }; + + // raylib은 다양한 파일 형식에서 모델, 애니메이션, 이미지 및 사운드를 로드하는 것을 지원합니다. + Model myModel = LoadModel("my_model.obj"); + Font someFont = LoadFont("some_font.ttf"); + + // 100x100 렌더 텍스처 생성 + RenderTexture renderTexture = LoadRenderTexture(100, 100); + + // WindowShouldClose는 사용자가 창을 닫고 있는지 확인합니다. + // 이는 바로 가기, 창 컨트롤 또는 이전에 설정한 키를 사용하여 발생할 수 있습니다. + while (!WindowShouldClose()) + { + + // 모든 그리기 호출 전에 BeginDrawing을 호출해야 합니다. + BeginDrawing(); + { + + // 배경을 특정 색상으로 지웁니다. + ClearBackground(BLACK); + + if (IsKeyDown(KEY_SPACE)) + DrawCircle(400, 400, 30, GREEN); + + // 간단한 텍스트 그리기 + DrawText("Congrats! You created your first window!", + 190, // x + 200, // y + 20, // 글꼴 크기 + LIGHTGRAY + ); + + // 대부분의 함수에는 여러 버전이 있습니다. + // 이들은 일반적으로 Ex, Pro, V + // 또는 때로는 Rec, Wires (3D만), Lines (2D만) 접미사가 붙습니다. + DrawTextEx(someFont, + "Text in another font", + (Vector2) {10, 10}, + 20, // 글꼴 크기 + 2, // 간격 + LIGHTGRAY); + + // 3D 그리기에 필요하며 2D와 동일합니다. + BeginMode3D(camera); + { + + DrawCube((Vector3) {0.0f, 0.0f, 3.0f}, + 1.0f, 1.0f, 1.0f, RED); + + // 그릴 때 흰색 색조는 원래 색상을 유지합니다. + DrawModel(myModel, (Vector3) {0.0f, 0.0f, 3.0f}, + 1.0f, // 스케일 + WHITE); + + } + // 3D 모드를 종료하여 다시 정상적으로 그릴 수 있습니다. + EndMode3D(); + + // 렌더 텍스처에 그리기 시작 + BeginTextureMode(renderTexture); + { + + // `BeginDrawing()`을 호출한 것과 동일하게 작동합니다. + + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + { + + DrawGrid(10, // 슬라이스 + 1.0f // 간격 + ); + + } + EndMode3D(); + + } + EndTextureMode(); + + // 렌더 텍스처에는 Texture2D 필드가 있습니다. + DrawTexture(renderTexture.texture, 40, 378, BLUE); + + } + EndDrawing(); + } + + // 로드된 객체 언로드 + UnloadFont(someFont); + UnloadModel(myModel); + + // 창 및 OpenGL 컨텍스트 닫기 + CloseWindow(); + + return 0; +} +``` + +## 더 읽을거리 +raylib에는 [훌륭한 예제](https://www.raylib.com/examples.html)가 있습니다. +C를 좋아하지 않는다면 [raylib 바인딩](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)을 확인하십시오. \ No newline at end of file diff --git a/ko/rdf.md b/ko/rdf.md new file mode 100644 index 0000000000..9b2dcef385 --- /dev/null +++ b/ko/rdf.md @@ -0,0 +1,125 @@ +--- +name: RDF +filename: learnrdf.ttl +contributors: +- ["Bob DuCharme", "http://bobdc.com/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +RDF(Resource Description Framework)는 [W3C 표준](https://www.w3.org/TR/2014/REC-rdf11-concepts-20140225/) 데이터 모델입니다. W3C는 여러 RDF 구문을 표준화했습니다. 아래 예제는 가장 인기 있는 [Turtle](https://www.w3.org/TR/turtle/)을 사용합니다. + +Turtle 파일의 한 가지 장점은 두 개의 구문적으로 유효한 Turtle 파일을 연결하면 또 다른 구문적으로 유효한 Turtle 파일이 생성된다는 것입니다. 이는 데이터 통합을 용이하게 하는 RDF의 여러 장점 중 하나입니다. + +RDF 데이터셋에 대한 W3C 표준 쿼리 언어는 [SPARQL](https://www.w3.org/TR/sparql11-query/)입니다. + +RDF는 모든 사실을 트리플로 알려진 세 부분으로 구성된 {주어, 술어, 목적어} 문으로 표현합니다. 동일한 엔티티가 일부 트리플의 주어이고 다른 트리플의 목적어일 수 있으므로, 트리플 집합은 그래프 데이터 구조를 나타낼 수 있습니다. 트리플에 대한 대규모 저장 시스템을 트리플스토어라고 하며, NoSQL 데이터베이스의 그래프 데이터베이스 범주에 속합니다. + +RDF 주어와 술어는 URI(Uniform Resource Identifiers)여야 합니다. URI는 일반적으로 URL처럼 보이지만, 로케이터가 아닌 식별자로 기능합니다. URI를 사용하면 리소스 식별자에 대한 컨텍스트를 제공하여 모호성을 없앨 수 있습니다. 예를 들어, 책 제목과 직책을 구별할 수 있습니다. + +``` +# 해시 기호는 주석 구분 기호입니다. + +# Turtle 트리플 문은 자연어 문장처럼 마침표로 끝납니다. + +# 다음 두 트리플은 신화적인 Example Company의 직원 134가 2022-11-12에 고용되었고 성이 Smith임을 알려줍니다: + + "2022-11-12" . + "Smith" . + +# 네임스페이스를 대신하는 접두사를 선언하면 장황함이 줄어듭니다. 이러한 선언은 일반적으로 파일 시작 부분에 오지만, 접두사를 선언하기 전에 처음 사용해야 한다는 요구 사항만 있습니다. + +@prefix ex: . +ex:emp134 ex:hireDate "2022-11-12" . +ex:emp134 ex:familyName "Smith" . + +# 세미콜론은 다음 트리플이 이전 트리플과 동일한 주어를 사용함을 의미합니다. +# 이는 단일 리소스에 대한 데이터를 나열하는 데 편리합니다. 다음 예제는 이전 예제와 동일한 의미를 가집니다. + +@prefix ex: . +ex:emp134 ex:hireDate "2022-11-12" ; + ex:familyName "Smith" . + +# 쉼표는 다음 트리플이 이전 트리플과 동일한 주어와 술어를 가짐을 의미합니다. + +ex:emp134 ex:nickname "Smithy", "Skipper", "Big J". + +# 값의 시작과 끝에 세 개의 단일 또는 이중 따옴표를 사용하면 여러 줄 문자열 값을 정의할 수 있습니다. + +ex:emp134 ex:description """ +Skipper는 11월에 회사에 입사했습니다. + +그는 항상 모두에게 농담을 합니다.""" . + +# 기존 표준 어휘 네임스페이스에서 URI를 사용하면 데이터 통합 및 이미 존재하는 많은 RDF와의 상호 운용성이 용이해집니다. 표준 및 로컬 사용자 정의 네임스페이스를 혼합하여 사용하는 것이 일반적입니다. + +@prefix vcard: . +ex:emp134 ex:hireDate "2022-11-12" ; + vcard:family-name "Smith" . + +# RDF 스키마 표준의 rdfs:label 술어는 엔티티의 사람이 읽을 수 있는 이름을 나타내는 일반적인 방법입니다. + +@prefix rdfs: . +ex:hireDate rdfs:label "hire date" . + +# 문자열 객체 값에는 언어 코드가 포함될 수 있어, +# 데이터를 읽는 애플리케이션(예: 사용자 인터페이스를 생성할 때)에서 엔티티의 다국어 표현을 더 쉽게 만듭니다. + +ex:hireDate rdfs:label "hire date"@en, "date d'embauche"@fr . + +# 트리플의 객체를 URI(또는 접두사 이름)로 나타내는 것은 필수는 아니지만, +# 트리플을 그래프로 연결할 수 있습니다. + +ex:emp134 vcard:family-name "Smith" . +ex:emp113 vcard:family-name "Jones" ; + ex:reportsTo ex:emp134 . + +# 객체는 XML 스키마 파트 2 표준의 데이터 유형이거나 사용자 정의 데이터 유형일 수 있습니다. + +@prefix xsd: . +ex:emp134 vcard:family-name "Smith"^^xsd:string ; # 기본 데이터 유형 + ex:hireDate "2022-11-12"^^xsd:date ; + ex:rating "3.5"^^ex:someCustomType . + +# RDF에서 스키마 사용은 선택 사항입니다. 스키마는 데이터셋의 전체 또는 일부를 설명할 수 있습니다. 일반적으로 rdfs 접두사를 사용하여 W3C RDF 스키마(RDFS) 표준에서 설명하는 어휘를 사용합니다. + +# 이러한 스키마는 새 데이터셋을 수용하기 쉽게 하기 위한 설명적인 것이며, +# 새 데이터를 생성하는 방법에 대한 규범적인 규칙이 아닙니다. 다음은 클래스를 선언합니다. +# (RDFS 자체도 트리플로 표현됩니다.) + +@prefix rdf: . +ex:Person rdf:type rdfs:Class . + +# 다음 트리플은 이전 트리플과 동일한 의미를 가지지만, +# 간결성과 가독성을 위해 Turtle 단축키를 사용합니다. + +ex:Person a rdfs:Class . + +# 마지막 트리플은 ex:Person이 클래스의 인스턴스임을 선언하고, +# 다음은 직원 113이 클래스 Employee의 인스턴스임을 선언합니다. + +ex:emp113 a ex:Employee . + +# 다음 네 개의 트리플을 읽고 RDFS를 이해하는 RDF 파서는 +# ex:emp113이 ex:Person의 인스턴스임을 추론합니다. 왜냐하면 +# ex:Employee의 인스턴스이고, ex:Person의 하위 클래스이기 때문입니다. + +ex:Employee a rdfs:Class . +ex:Employee rdfs:subClassOf ex:Person . + +# RDFS를 사용하면 속성을 선언하고 클래스와 연결할 수 있습니다. +# 속성은 일급 리소스이며 객체 지향적 의미에서 클래스에 "속하지" 않습니다. +# rdfs:domain은 "다음 객체 클래스가 이 트리플의 주어가 지정하는 속성을 사용한다"는 의미입니다. +# rdfs:range는 "이 트리플의 주어가 지정하는 속성은 다음 클래스 또는 유형의 값을 가질 것이다"는 의미입니다. + +ex:birthday rdf:type rdf:Property ; + rdfs:domain ex:Person ; + rdfs:range xsd:date . +``` + +## 더 읽을거리 + +* [RDF Primer — Turtle 버전](https://www.w3.org/2007/02/turtle/primer/) W3C에서 +* [RDF란 무엇인가?](https://www.bobdc.com/blog/whatisrdf/) bobdc.com에서 +* [RDFS란 무엇인가?](https://www.bobdc.com/blog/whatisrdfs/) bobdc.com에서 +* [RDF 및 SPARQL 소개](https://data.europa.eu/sites/default/files/d2.1.2_training_module_1.3_introduction_to_rdf_sparql_en_edp.pdf) data.europa.eu에서 \ No newline at end of file diff --git a/ko/reason.md b/ko/reason.md new file mode 100644 index 0000000000..8e076f1908 --- /dev/null +++ b/ko/reason.md @@ -0,0 +1,531 @@ +--- +name: Reason +filename: reason.re +contributors: + - ["Seth Corker", "https://sethcorker.com"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Reason은 OCaml 위에 구축된 구문으로, JavaScript와 같은 C 스타일 구문에 익숙한 프로그래머가 쉽게 시작할 수 있도록 합니다. BuckleScript는 Reason을 JavaScript로 컴파일하는 도구 체인의 일부이므로 JavaScript가 실행되는 모든 곳에서 정적 타입 코드를 작성할 수 있습니다. + +```reason +/* 주석은 슬래시-별표로 시작하고 별표-슬래시로 끝납니다. */ + +/*---------------------------------------------- + * 변수 및 함수 선언 + *---------------------------------------------- + * 변수 및 함수는 let 키워드를 사용하고 세미콜론으로 끝납니다. + * `let` 바인딩은 불변입니다. + */ + +let x = 5; +/* - 타입을 추가하지 않았습니다. Reason은 x가 int임을 추론합니다. */ + +/* 이와 같은 함수는 두 개의 인수를 받아 더합니다. */ +let add = (a, b) => a + b; +/* - 이것도 타입 주석이 필요 없습니다! */ + +/*---------------------------------------------- + * 타입 주석 + *---------------------------------------------- + * 대부분의 경우 타입을 명시적으로 주석을 달 필요는 없지만, + * 필요한 경우 이름 뒤에 타입을 추가할 수 있습니다. + */ + +/* 타입은 다음과 같이 명시적으로 작성할 수 있습니다. */ +let x: int = 5; + +/* 이전의 add 함수도 명시적으로 주석을 달 수 있습니다. */ +let add2 = (a: int, b: int): int => a + b; + +/* type 키워드를 사용하여 타입을 별칭으로 지정할 수 있습니다. */ +type companyId = int; +let myId: companyId = 101; + +/* Reason에서는 변형이 권장되지 않지만 필요한 경우 사용할 수 있습니다. + let 바인딩을 변형해야 하는 경우 값은 `ref()`로 래핑되어야 합니다. */ +let myMutableNumber = ref(120); + +/* 값(ref 컨테이너가 아님)에 액세스하려면 `^`를 사용하십시오. */ +let copyOfMyMutableNumber = myMutableNumber^; + +/* 새 값을 할당하려면 `:=` 연산자를 사용하십시오. */ +myMutableNumber := 240; + +/*---------------------------------------------- + * 기본 타입 및 연산자 + *---------------------------------------------- + */ + +/* > 문자열 */ + +/* 문자열에는 큰따옴표를 사용하십시오. */ +let greeting = "Hello world!"; + +/* 문자열은 여러 줄에 걸쳐 있을 수 있습니다. */ +let aLongerGreeting = "저를 보세요, +저는 여러 줄 문자열입니다. +"; + +/* 인용된 문자열은 문자열 보간 및 특수 문자에 사용할 수 있습니다. + 유니코드에는 `js` 주석을 사용하십시오. */ +let world = {js|🌍|js}; + +/* `j` 주석은 문자열 보간에 사용됩니다. */ +let helloWorld = {j|hello, $world|j}; + +/* ++로 문자열 연결 */ +let name = "John " ++ "Wayne"; +let emailSubject = "Hi " ++ name ++ ", you're a valued customer"; + +/* > 문자 */ + +/* 문자 유형에는 단일 문자를 사용하십시오. */ +let lastLetter = 'z'; +/* - 문자는 유니코드 또는 UTF-8을 지원하지 않습니다. */ + +/* > 부울 */ + +/* 부울은 true 또는 false일 수 있습니다. */ +let isLearning = true; + +true && false; /* - : bool = false; 논리곱 */ +true || true; /* - : bool = true; 논리합 */ +!true; /* - : bool = false; 논리 부정 */ + +/* 보다 큼 `>`, 또는 크거나 같음 `>=` */ +'a' > 'b'; /* - bool : false */ + +/* 보다 작음 `<`, 또는 작거나 같음 `<=` */ +1 < 5; /* - : bool = true */ + +/* 구조적 같음 */ +"hello" == "hello"; /* - : bool = true */ + +/* 참조적 같음 */ +"hello" === "hello"; /* - : bool = false */ +/* - 두 개의 다른 "hello" 문자열 리터럴이기 때문에 false입니다. */ + +/* 구조적 같지 않음 */ +lastLetter != 'a'; /* -: bool = true */ + +/* 참조적 같지 않음 */ +lastLetter !== lastLetter; /* - : bool = false */ + +/* > 정수 */ +/* 정수에 대한 수학 연산 수행 */ + +1 + 1; /* - : int = 2 */ +25 - 11; /* - : int = 11 */ +5 * 2 * 3; /* - : int = 30 */ +8 / 2; /* - : int = 4 */ + +/* > 부동 소수점 */ +/* 부동 소수점 연산자 뒤에는 점이 붙습니다. */ + +1.1 +. 1.5; /* - : float = 2.6 */ +18.0 -. 24.5; /* - : float = -6.5 */ +2.5 *. 2.0; /* - : float = 5. */ +16.0 /. 4.0; /* - : float = 4. */ + +/* > 튜플 + * 튜플은 다음 속성을 가집니다. + - 불변 + - 순서 지정 + - 생성 시 고정 크기 + - 이기종 (다른 유형의 값을 포함할 수 있음) + 튜플은 2개 이상의 값입니다. */ + +let teamMember = ("John", 25); + +/* 값과 일치하는 타입 주석 */ +let position2d: (float, float) = (9.0, 12.0); + +/* 패턴 매칭은 관심 있는 값만 검색하는 훌륭한 도구입니다. + y 값만 원한다면 `_`를 사용하여 값을 무시합니다. */ +let (_, y) = position2d; +y +. 1.0; /* - : float = 13. */ + +/* > 레코드 */ + +/* 레코드는 명시적인 유형을 가져야 합니다. */ +type trainJourney = { + destination: string, + capacity: int, + averageSpeed: float, +}; + +/* 유형이 선언되면 Reason은 필요할 때마다 유형을 추론할 수 있습니다. */ +let firstTrip = {destination: "London", capacity: 45, averageSpeed: 120.0}; + +/* 점 표기법을 사용하여 속성에 액세스합니다. */ +let maxPassengers = firstTrip.capacity; + +/* 레코드 유형을 다른 파일에 정의하는 경우 Trips.re라는 파일에 trainJourney가 있었다면 파일 이름을 참조해야 합니다. */ +let secondTrip: Trips.trainJourney = { + destination: "Paris", + capacity: 50, + averageSpeed: 150.0, +}; + +/* 레코드는 기본적으로 불변입니다. */ +/* 그러나 레코드의 내용은 스프레드 연산자를 사용하여 복사할 수 있습니다. */ +let newTrip = {...secondTrip, averageSpeed: 120.0}; + +/* 레코드 속성은 `mutable` 키워드로 명시적으로 변경할 수 있습니다. */ +type breakfastCereal = { + name: string, + mutable amount: int, +}; + +let tastyMuesli = {name: "Tasty Muesli TM", amount: 500}; + +tastyMuesli.amount = 200; +/* - tastyMuesli의 양은 이제 200입니다. */ + +/* Punning은 중복 타이핑을 피하는 데 사용됩니다. */ +let name = "Just As Good Muesli"; +let justAsGoodMuesli = {name, amount: 500}; +/* - justAsGoodMuesli.name은 이제 "Just As Good Muesli"이며, 이는 + { name: name, amount: 500 }과 동일합니다. */ + +/* > 변형 + 상호 배타적인 상태는 변형으로 표현할 수 있습니다. */ + +type authType = + | GitHub + | Facebook + | Google + | Password; +/* - 생성자는 대문자로 시작해야 합니다. */ +/* - 레코드와 마찬가지로 변형은 다른 파일에 선언된 경우 이름을 지정해야 합니다. */ + +let userPreferredAuth = GitHub; + +/* 변형은 switch 문과 함께 사용하기에 좋습니다. */ +let loginMessage = + switch (userPreferredAuth) { + | GitHub => "GitHub 자격 증명으로 로그인." + | Facebook => "Facebook 계정으로 로그인." + | Google => "Google 계정으로 로그인" + | Password => "이메일과 비밀번호로 로그인." + }; + +/* > 옵션 + 옵션은 None 또는 Some('a)일 수 있으며, 여기서 'a는 유형입니다. */ + +let userId = Some(23); + +/* switch는 두 가지 경우를 처리합니다. */ +let alertMessage = + switch (userId) { + | Some(id) => "환영합니다. 귀하의 ID는" ++ string_of_int(id) + | None => "계정이 없습니다!" + }; +/* - `None` 또는 `Some` 케이스가 누락되면 오류가 발생합니다. */ + +/* > 목록 + * 목록은 다음 속성을 가집니다. + - 불변 + - 순서 지정 + - 항목을 앞에 추가하는 속도 + - 분할 속도 + + * Reason의 목록은 연결 목록입니다. + */ + +/* 목록은 대괄호로 선언됩니다. */ +let userIds = [1, 4, 8]; + +/* 유형은 list('a)로 명시적으로 설정할 수 있으며, 여기서 'a는 유형입니다. */ +type idList = list(int); +type attendanceList = list(string); + +/* 목록은 불변입니다. */ +/* 그러나 목록의 내용은 스프레드 연산자를 사용하여 복사할 수 있습니다. */ +let newUserIds = [101, 102, ...userIds]; + +/* > 배열 + * 배열은 다음 속성을 가집니다. + - 변경 가능 + - 임의 접근 및 업데이트 속도 */ + +/* 배열은 `[|`로 선언되고 `|]`로 끝납니다. */ +let languages = [|"Reason", "JavaScript", "OCaml"|]; + +/*---------------------------------------------- + * 함수 + *---------------------------------------------- + */ + +/* Reason 함수는 화살표 구문을 사용하며, 표현식이 반환됩니다. */ +let signUpToNewsletter = email => "Thanks for signing up " ++ email; + +/* 다음과 같이 함수를 호출합니다. */ +signUpToNewsletter("hello@reason.org"); + +/* 더 긴 함수에는 블록을 사용하십시오. */ +let getEmailPrefs = email => { + let message = "Update settings for " ++ email; + let prefs = ["Weekly News", "Daily Notifications"]; + + (message, prefs); +}; +/* - 최종 튜플은 암시적으로 반환됩니다. */ + +/* > 레이블이 지정된 인수 */ + +/* 인수는 ~ 기호로 레이블을 지정할 수 있습니다. */ +let moveTo = (~x, ~y) => {/* x,y로 이동 */}; + +moveTo(~x=7.0, ~y=3.5); + +/* 레이블이 지정된 인수는 함수 내에서 사용되는 이름을 가질 수도 있습니다. */ +let getMessage = (~message as msg) => "==" ++ msg ++ "=="; + +getMessage(~message="You have a message!"); +/* - 호출자는 ~message를 지정하지만 내부적으로 함수는 이를 사용할 수 있습니다. */ + +/* 다음 함수도 명시적으로 유형이 선언되어 있습니다. */ +let showDialog = (~message: string): unit => { + () /* 대화 상자 표시 */; +}; +/* - 반환 유형은 `unit`이며, 이 함수가 값을 반환하지 않음을 지정하는 특수 유형입니다. + `unit` 유형은 `()`로도 표현할 수 있습니다. */ + +/* > 커링 + 함수는 커링될 수 있으며 부분적으로 호출되어 쉽게 재사용할 수 있습니다. */ + +let div = (denom, numr) => numr / denom; +let divBySix = div(6); +let divByTwo = div(2); + +div(3, 24); /* - : int = 8 */ +divBySix(128); /* - : int = 21 */ +divByTwo(10); /* - : int = 5 */ + +/* > 선택적 레이블 인수 */ + +/* 선택적 레이블 인수에 `=?` 구문을 사용하십시오. */ +let greetPerson = (~name, ~greeting=?, ()) => { + switch (greeting) { + | Some(greet) => greet ++ " " ++ name + | None => "Hi " ++ name + }; +}; +/* - 세 번째 인수 `unit` 또는 `()`는 필수입니다. 이를 생략하면 + 함수가 커링되어 greetPerson(~name="Kate")가 부분 함수를 생성합니다. + 이를 해결하려면 선언 및 호출 시 `unit`을 추가하십시오. */ + +/* 선택적 레이블 인수 없이 greetPerson 호출 */ +greetPerson(~name="Kate", ()); + +/* 모든 인수로 greetPerson 호출 */ +greetPerson(~name="Marco", ~greeting="How are you today,"); + +/* > 파이프 */ +/* 함수는 파이프라인 연산자로 호출할 수 있습니다. */ + +/* 첫 번째 인수를 전달하려면 `->`를 사용하십시오(파이프-첫 번째). */ +3->div(24); /* - : int = 8 */ +/* - 이것은 div(3, 24)와 동일합니다. */ + +36->divBySix; /* - : int = 6 */ +/* - 이것은 divBySix(36)와 동일합니다. */ + +/* 파이프는 코드를 함께 연결하기 쉽게 만듭니다. */ +let addOne = a => a + 1; +let divByTwo = a => a / 2; +let multByThree = a => a * 3; + +let pipedValue = 3->addOne->divByTwo->multByThree; /* - : int = 6 */ + +/*---------------------------------------------- + * 제어 흐름 및 패턴 매칭 + *---------------------------------------------- + */ + +/* > If-else */ +/* Reason에서 `If`는 결과를 반환하는 표현식입니다. */ + +/* greeting은 "Good morning!"이 됩니다. */ +let greeting = if (true) {"Good morning!"} else {"Hello!"}; + +/* else 분기가 없으면 표현식은 `unit` 또는 `()`를 반환합니다. */ +if (false) { + showDialog(~message="정말 떠나시겠습니까?"); +}; +/* - 결과가 `unit` 유형이므로 결과를 할당하려면 두 반환 유형이 동일해야 합니다. */ + +/* > 구조 분해 */ +/* 데이터 구조에서 속성을 쉽게 추출합니다. */ + +let aTuple = ("Teacher", 101); + +/* 튜플의 값을 추출할 수 있습니다. */ +let (name, classNum) = aTuple; + +/* 레코드의 속성도 추출할 수 있습니다. */ +type person = { + firstName: string, + age: int, +}; +let bjorn = {firstName: "Bjorn", age: 28}; + +/* 변수 이름은 레코드 속성 이름과 일치해야 합니다. */ +let {firstName, age} = bjorn; + +/* 그러나 다음과 같이 이름을 바꿀 수 있습니다. */ +let {firstName: bName, age: bAge} = bjorn; + +let {firstName: cName, age: _} = bjorn; + +/* > Switch + 스위치를 사용한 패턴 매칭은 Reason의 중요한 도구입니다. + 표현적이고 간결한 도구를 위해 구조 분해와 함께 사용할 수 있습니다. */ + +/* 간단한 목록을 가져옵니다. */ +let firstNames = ["James", "Jean", "Geoff"]; + +/* 처리하려는 각 경우에 대해 이름을 패턴 일치시킬 수 있습니다. */ +switch (firstNames) { +| [] => "이름 없음" +| [first] => "오직 " ++ first +| [first, second] => "두 개의 이름 " ++ first ++ "," ++ second +| [first, second, third] => + "세 개의 이름, " ++ first ++ ", " ++ second ++ ", " ++ third +| _ => "많은 이름" +}; +/* - `_`는 끝에 있는 모든 것을 포괄하는 것으로, 값에 상관없이 모든 다른 경우와 일치합니다. */ + +/* > When 절 */ + +let isJohn = a => a == "John"; +let maybeName = Some("John"); + +/* When은 간단한 스위치에 더 복잡한 논리를 추가할 수 있습니다. */ +let aGreeting = + switch (maybeName) { + | Some(name) when isJohn(name) => "안녕하세요 존! 잘 지내세요?" + | Some(name) => "안녕하세요 " ++ name ++ ", 환영합니다." + | None => "인사할 사람이 없습니다." + }; + +/* > 예외 */ + +/* 사용자 정의 예외 정의 */ +exception Under_Age; + +/* 함수 내에서 예외 발생 */ +let driveToTown = (driver: person) => + if (driver.age >= 15) { + "우리는 마을에 있습니다."; + } else { + raise(Under_Age); + }; + +let evan = {firstName: "Evan", age: 14}; + +/* Under_Age 예외에 대한 패턴 일치 */ +switch (driveToTown(evan)) { +| status => print_endline(status) +| exception Under_Age => + print_endline(evan.firstName ++ "은 운전하기에 너무 어립니다!") +}; + +/* 또는 try 블록을 사용할 수 있습니다. */ +/* - Reason 예외는 옵션으로 피할 수 있으며 거의 사용되지 않습니다. */ +let messageToEvan = + try (driveToTown(evan)) { + | Under_Age => evan.firstName ++ "은 운전하기에 너무 어립니다!" + }; + +/*---------------------------------------------- + * 객체 + *---------------------------------------------- + * 객체는 레코드 유형과 유사하지만 덜 엄격합니다. + * 객체는 클래스와 유사합니다. + */ + +/* 객체는 레코드처럼 유형이 지정될 수 있지만 점을 포함합니다. */ +type surfaceComputer = { + . + color: string, + capacity: int, +}; +/* - 단일 점은 닫힌 객체를 의미하며, 이 유형을 사용하는 객체는 정확한 모양을 가져야 합니다. */ + +let surfaceBook: surfaceComputer = {pub color = "blue"; pub capacity = 512}; + +/* 그러나 객체는 유형이 필요하지 않습니다. */ +let house = { + /* 개인 속성 */ + val temp = ref(18.0); + /* 공용 속성 */ + pub temperature = temp; + /* house 내에서만 액세스할 수 있는 개인 메서드 */ + pri setThermostat = v => temp := v; + /* 개인 setThermostat 메서드를 호출하는 공용 메서드 */ + pub arriveHome = () => this#setThermostat(22.0) +}; + +house#temperature; /* - : float = 18. */ +house#arriveHome(); +house#temperature; /* - : float = 22. */ + +/*---------------------------------------------- + * 모듈 + *---------------------------------------------- + * 모듈은 코드를 구성하고 네임스페이스를 제공하는 데 사용됩니다. + * 각 파일은 기본적으로 모듈입니다. + */ + +/* 모듈 생성 */ +module Staff = { + type role = + | Delivery + | Sales + | Other; + type member = { + name: string, + role, + }; + + let getRoleDirectionMessage = staff => + switch (staff.role) { + | Delivery => "진심으로 배달하십시오!" + | Sales => "당신만이 할 수 있는 것처럼 판매하십시오!" + | Other => "당신은 팀의 중요한 부분입니다!" + }; +}; + +/* 모듈은 점 표기법으로 액세스할 수 있습니다. */ +let newEmployee: Staff.member = {name: "Laura", role: Staff.Delivery}; + +/* 모듈 이름을 사용하는 것이 번거로울 수 있으므로 `open`을 사용하여 모듈의 내용을 현재 범위로 열 수 있습니다. */ +open Staff; + +let otherNewEmployee: member = {name: "Fred", role: Other}; + +/* 모듈은 `include` 키워드를 사용하여 확장할 수 있습니다. include는 새 모듈의 범위에 모듈의 내용을 복사합니다. */ +module SpecializedStaff = { + include Staff; + + /* `member`가 포함되어 있으므로 명시적으로 참조할 필요가 없습니다. */ + let ceo: member = {name: "Reggie", role: Other}; + + let getMeetingTime = staff => + switch (staff) { + | Other => 11_15 /* - : int = 1115; 밑줄은 서식 지정용입니다. */ + | _ => 9_30 + }; +}; +``` + +## 더 읽을거리 + +- [공식 Reason 문서](https://reasonml.github.io/docs/en/what-and-why) +- [공식 BuckleScript 문서](https://bucklescript.github.io/docs/en/what-why) +- [Reason 사용해 보기](https://reasonml.github.io/en/try) +- [Nik Graf의 Reason 시작하기](https://egghead.io/courses/get-started-with-reason) \ No newline at end of file diff --git a/ko/red.md b/ko/red.md new file mode 100644 index 0000000000..0851c55cd1 --- /dev/null +++ b/ko/red.md @@ -0,0 +1,205 @@ +--- +name: Red +filename: learnred.red +contributors: + - ["Arnold van Hofwegen", "https://github.com/iArnold"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Red는 작업을 완료해야 할 필요성에서 만들어졌으며, 저자가 사용하고 싶었던 도구인 REBOL 언어에는 몇 가지 단점이 있었습니다. +그 당시에는 오픈 소스가 아니었고, 컴파일된 언어에 비해 평균적으로 느린 해석 언어였습니다. + +Red는 C 수준의 방언인 Red/System과 함께 프로그래밍에 필요한 전체 프로그래밍 공간을 다루는 언어를 제공합니다. +Red는 REBOL 언어를 기반으로 하는 언어입니다. Red 자체는 REBOL 언어의 유연성을 재현하며, Red가 기반으로 구축될 기본 언어인 Red/System은 C와 같이 금속에 더 가까운 프로그래밍의 더 기본적인 요구 사항을 다룹니다. + +Red는 세계 최초의 풀 스택 프로그래밍 언어가 될 것입니다. 이는 다른 스택 도구의 도움 없이 금속에서 메타까지 모든 수준에서 (거의) 모든 프로그래밍 작업을 수행할 수 있는 효과적인 도구가 될 것임을 의미합니다. +또한 Red는 GCC와 같은 도구 체인 없이 모든 플랫폼에서 다른 모든 플랫폼으로 Red 소스 코드를 교차 컴파일할 수 있습니다. 그리고 이 모든 것을 1MB 미만의 바이너리 실행 파일에서 수행할 것입니다. + +첫 번째 Red를 배울 준비가 되셨습니까? + +``` +헤더 앞의 모든 텍스트는 주석으로 처리됩니다. 단, 이 헤더 앞 텍스트에서 대문자 "R"로 시작하는 "red"라는 단어를 사용하지 않는 한 말이죠. +이것은 사용된 렉서의 일시적인 단점이지만, 대부분의 경우 스크립트나 프로그램을 헤더 자체로 시작합니다. + +빨간색 스크립트의 헤더는 대문자 "red" 뒤에 공백 문자, 그 뒤에 대괄호 블록 []이 옵니다. 대괄호 블록은 이 스크립트 또는 프로그램에 대한 유용한 정보로 채울 수 있습니다: +작성자 이름, 파일 이름, 버전, 라이선스, 프로그램 요약 또는 필요한 기타 파일. red/System 헤더는 red 헤더와 동일하며, "red/System"이라고만 표시하고 "red"라고 표시하지 않습니다. +``` + +```red +Red [] + +;이것은 주석 처리된 줄입니다. + +print "Hello Red World" ; 이것은 또 다른 주석입니다. + +comment { + 이것은 여러 줄 주석입니다. + 방금 Red 버전의 "Hello World" 프로그램을 보셨습니다. +} + +; 프로그램의 진입점은 발견된 첫 번째 실행 가능한 코드입니다. +; 'main' 함수로 제한할 필요가 없습니다. + +; 유효한 변수 이름은 문자로 시작하며 숫자를 포함할 수 있습니다. +; 대문자 A부터 F와 숫자만 포함하고 'h'로 끝나는 변수는 금지됩니다. +; 이는 Red 및 Red/System에서 16진수를 표현하는 방식이기 때문입니다. + +; 콜론 ":"을 사용하여 변수에 값을 할당합니다. +my-name: "Red" +reason-for-using-the-colon: {콜론을 사용하여 값을 할당하면 + 등호 "="는 비교 목적으로만 사용할 수 있게 됩니다. + "="가 원래 의도했던 바와 정확히 일치합니다! + 학교에서 배웠던 y = x + 1 및 x = 1 => y = 2와 같은 것을 기억하십니까? +} +is-this-name-valid?: true + +; print를 사용하여 출력을 인쇄하거나, 인쇄된 텍스트 끝에 줄 바꿈이나 줄 바꿈 없이 인쇄하려면 prin을 사용합니다. + +prin " My name is " print my-name +My name is Red + +print ["My name is " my-name lf] +My name is Red + +; 이미 눈치채셨겠지만: 문장은 세미콜론으로 끝나지 않습니다 ;-) + +; +; 데이터 유형 +; +; Rebol을 아신다면 아마도 많은 데이터 유형이 있다는 것을 눈치채셨을 겁니다. Red는 아직 모든 유형을 가지고 있지 않지만, Rebol에 가깝기를 원하므로 많은 데이터 유형을 가질 것입니다. +; 느낌표로 유형을 인식할 수 있습니다. 하지만 이름이 느낌표로 끝나는 것도 허용됩니다. +; 사용 가능한 유형 중 일부는 integer! string! block!입니다. + +; 사용하기 전에 변수를 선언해야 합니까? +; Red는 사용하려는 데이터에 가장 적합한 변수를 스스로 알고 있습니다. +; 변수 선언은 항상 필요한 것은 아닙니다. +; 변수를 선언하고 유형을 지정하는 것이 좋은 코딩 관행으로 간주되지만, +; Red는 이를 강요하지 않습니다. +; 변수를 선언하고 유형을 지정할 수 있습니다. 변수의 유형은 +; 바이트 단위의 크기를 결정합니다. + +; integer! 유형의 변수는 일반적으로 4바이트 또는 32비트입니다. +my-integer: 0 +; Red의 정수는 부호가 있습니다. 현재는 부호 없는 정수를 지원하지 않지만, 나중에 지원될 예정입니다. + +; type?를 사용하여 변수의 유형을 확인합니다. +type? my-integer +integer! + +; 동시에 초기화되는 다른 변수를 사용하여 변수를 초기화할 수 있습니다. +; 여기서 초기화는 변수를 선언하고 값을 할당하는 것을 의미합니다. +i2: 1 + i1: 1 + +; 산술은 간단합니다. +i1 + i2 ; 결과 3 +i2 - i1 ; 결과 1 +i2 * i1 ; 결과 2 +i1 / i2 ; 결과 0 (0.5이지만 0으로 잘림) + +; 비교 연산자는 아마도 익숙할 것입니다. 다른 언어와 달리 +; 비교에는 단일 '=' 기호만 필요합니다. 부등호는 Pascal과 같이 '<>'입니다. +; Red에는 부울과 같은 유형이 있습니다. true 및 false 값을 가지지만, +; on/off 또는 yes/no 값도 사용할 수 있습니다. + +3 = 2 ; 결과 false +3 <> 2 ; 결과 true +3 > 2 ; 결과 true +3 < 2 ; 결과 false +2 <= 2 ; 결과 true +2 >= 2 ; 결과 true + +; +; 제어 구조 +; +; if +; 주어진 조건이 참이면 코드 블록을 평가합니다. IF는 +; 블록의 결과 값을 반환하거나 조건이 거짓이면 'none'을 반환합니다. +if a < 0 [print "a is negative"] + +; either +; 주어진 조건이 참이면 코드 블록을 평가하고, 그렇지 않으면 +; 대체 코드 블록을 평가합니다. 두 블록의 마지막 표현식이 +; 동일한 유형이면 EITHER를 표현식 내에서 사용할 수 있습니다. +either a > 0 [ + msg: "positive" +][ + either a = 0 [ + msg: "zero" + ][ + msg: "negative" + ] +] + +print ["a is " msg lf] + +; 이것을 작성하는 다른 방법이 있습니다. +; (모든 코드 경로가 동일한 유형의 값을 반환하므로 허용됩니다): + +msg: either a > 0 [ + "positive" +][ + either a = 0 [ + "zero" + ][ + "negative" + ] +] +print ["a is " msg lf] + +; until +; 블록 끝의 조건이 충족될 때까지 코드 블록을 반복합니다. +; UNTIL은 항상 마지막 표현식의 최종 평가에서 'true' 값을 반환합니다. +c: 5 +until [ + prin "o" + c: c - 1 + c = 0 ; until 루프를 끝내는 조건 +] +; 출력: +ooooo +; 조건이 처음부터 충족되지 않더라도 루프는 항상 최소 한 번 평가됩니다. + +; while +; 주어진 조건이 충족되는 동안 코드 블록을 평가합니다. +; WHILE은 값을 반환하지 않으므로 표현식에서 사용할 수 없습니다. +c: 5 +while [c > 0][ + prin "o" + c: c - 1 +] +; 출력: +ooooo + +; +; 함수 +; +; 함수 예제 +twice: function [a [integer!] /one return: [integer!]][ + c: 2 + a: a * c + either one [a + 1][a] +] +b: 3 +print twice b ; 6을 출력합니다. + +; 외부 파일을 #include로 가져오고 파일 이름은 % 기호로 시작합니다. +#include %includefile.red +; 이제 포함된 파일의 함수도 사용할 수 있습니다. +``` + +## 더 읽을거리 + +Red에 대한 주요 정보 출처는 [Red 언어 홈페이지](http://www.red-lang.org)입니다. + +소스는 [GitHub](https://github.com/red/red)에서 찾을 수 있습니다. + +Red/System 언어 사양은 [여기](http://static.red-lang.org/red-system-specs-light.html)에서 찾을 수 있습니다. + +Rebol과 Red에 대해 더 자세히 알아보려면 [Gitter 채팅](https://gitter.im/red/red)에 참여하십시오. 그리고 그것이 작동하지 않는다면 [Red 메일링 리스트](mailto: red-langNO_SPAM@googlegroups.com)로 메일을 보내십시오(NO_SPAM 제거). + +[Stack Overflow](https://stackoverflow.com/questions/tagged/red)에서 질문을 찾아보거나 질문하십시오. + +Red를 바로 사용해보고 싶으십니까? [try Rebol and Red 사이트](http://tryrebol.esperconsultancy.nl)에서 가능합니다. + +[Rebol](http://www.rebol.com/docs.html)을 배우면서 Red를 배울 수도 있습니다. \ No newline at end of file diff --git a/ko/rescript.md b/ko/rescript.md new file mode 100644 index 0000000000..eb404a3440 --- /dev/null +++ b/ko/rescript.md @@ -0,0 +1,528 @@ +--- +name: ReScript +filename: rescript.res +contributors: + - ["Seth Corker", "https://sethcorker.com"] + - ["Danny Yang", "https://yangdanny97.github.io/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +ReScript는 효율적이고 사람이 읽을 수 있는 JavaScript로 컴파일되는 강력한 타입 언어입니다. 어떤 코드베이스 크기에도 확장 가능한 번개처럼 빠른 컴파일러 도구 체인이 함께 제공됩니다. ReScript는 OCaml과 Reason에서 파생되었으며, 타입 추론 및 패턴 매칭과 같은 멋진 기능과 초보자에게 친숙한 구문 및 JavaScript 생태계에 중점을 둡니다. + +```javascript +/* 주석은 슬래시-별표로 시작하고 별표-슬래시로 끝납니다. */ +// 한 줄 주석은 이중 슬래시로 시작합니다. + +/*---------------------------------------------- + * 변수 및 함수 선언 + *---------------------------------------------- + * 변수 및 함수는 let 키워드를 사용하고 세미콜론으로 끝납니다. + * `let` 바인딩은 불변입니다. + */ + +let x = 5 +/* - 타입을 추가하지 않았습니다. ReScript는 x가 int임을 추론합니다. */ + +/* 이와 같은 함수는 두 개의 인수를 받아 더합니다. */ +let add = (a, b) => a + b +/* - 이것도 타입 주석이 필요 없습니다! */ + +/*---------------------------------------------- + * 타입 주석 + *---------------------------------------------- + * 대부분의 경우 타입을 명시적으로 주석을 달 필요는 없지만, + * 필요한 경우 이름 뒤에 타입을 추가할 수 있습니다. + */ + +/* 타입은 다음과 같이 명시적으로 작성할 수 있습니다. */ +let x: int = 5 + +/* 이전의 add 함수도 명시적으로 주석을 달 수 있습니다. */ +let add2 = (a: int, b: int): int => a + b + +/* type 키워드를 사용하여 타입을 별칭으로 지정할 수 있습니다. */ +type companyId = int +let myId: companyId = 101 + +/* Mutation은 ReScript에서 권장되지 않지만 필요한 경우 사용할 수 있습니다. + let 바인딩을 변형해야 하는 경우 값은 `ref()`로 래핑되어야 합니다. */ +let myMutableNumber = ref(120) + +/* 값(ref 컨테이너가 아님)에 액세스하려면 `.contents`를 사용하십시오. */ +let copyOfMyMutableNumber = myMutableNumber.contents + +/* 새 값을 할당하려면 `:=` 연산자를 사용하십시오. */ +myMutableNumber := 240 + +/*---------------------------------------------- + * 기본 타입 및 연산자 + *---------------------------------------------- + */ + +/* > 문자열 */ + +/* 문자열에는 큰따옴표를 사용하십시오. */ +let greeting = "Hello world!" + +/* 문자열은 여러 줄에 걸쳐 있을 수 있습니다. */ +let aLongerGreeting = "저를 보세요, +저는 여러 줄 문자열입니다. +" + +/* 유니코드에는 `를 사용하십시오. */ +let world = `🌍` + +/* ` 주석은 문자열 보간에도 사용됩니다. */ +let helloWorld = `hello, ${world}` +/* 바인딩은 문자열로 변환되어야 합니다. */ +let age = 10 +let ageMsg = `저는 ${Int.toString(age)}살입니다.` + + +/* ++로 문자열 연결 */ +let name = "John " ++ "Wayne" +let emailSubject = "Hi " ++ name ++ ", you're a valued customer" + +/* > 문자 */ + +/* 문자 유형에는 단일 문자를 사용하십시오. */ +let lastLetter = 'z' +/* - 문자는 유니코드 또는 UTF-8을 지원하지 않습니다. */ + +/* > 부울 */ + +/* 부울은 true 또는 false일 수 있습니다. */ +let isLearning = true + +true && false /* - : bool = false 논리곱 */ +true || true /* - : bool = true 논리합 */ +!true /* - : bool = false 논리 부정 */ + +/* 보다 큼 `>`, 또는 크거나 같음 `>=` */ +'a' > 'b' /* - bool : false */ + +/* 보다 작음 `<`, 또는 작거나 같음 `<=` */ +1 < 5 /* - : bool = true */ + +/* 구조적 같음 */ +"hello" == "hello" /* - : bool = true */ + +/* 참조적 같음 */ +"hello" === "hello" /* - : bool = false */ +/* - 두 개의 다른 "hello" 문자열 리터럴이기 때문에 false입니다. */ + +/* 구조적 같지 않음 */ +lastLetter != 'a' /* -: bool = true */ + +/* 참조적 같지 않음 */ +lastLetter !== lastLetter /* - : bool = false */ + +/* > 정수 */ +/* 정수에 대한 수학 연산 수행 */ + +1 + 1 /* - : int = 2 */ +25 - 11 /* - : int = 11 */ +5 * 2 * 3 /* - : int = 30 */ +8 / 2 /* - : int = 4 */ + +/* > 부동 소수점 */ +/* 부동 소수점 연산자 뒤에는 점이 붙습니다. */ + +1.1 +. 1.5 /* - : float = 2.6 */ +18.0 -. 24.5 /* - : float = -6.5 */ +2.5 *. 2.0 /* - : float = 5. */ +16.0 /. 4.0 /* - : float = 4. */ + +/* > 튜플 + * 튜플은 다음 속성을 가집니다. + - 불변 + - 순서 지정 + - 생성 시 고정 크기 + - 이기종 (다른 유형의 값을 포함할 수 있음) + 튜플은 2개 이상의 값입니다. */ + +let teamMember = ("John", 25) + +/* 값과 일치하는 타입 주석 */ +let position2d: (float, float) = (9.0, 12.0) + +/* 패턴 매칭은 관심 있는 값만 검색하는 훌륭한 도구입니다. + y 값만 원한다면 `_`를 사용하여 값을 무시합니다. */ +let (_, y) = position2d +y +. 1.0 /* - : float = 13. */ + +/* > 레코드 */ + +/* 레코드는 명시적인 유형을 가져야 합니다. */ +type trainJourney = { + destination: string, + capacity: int, + averageSpeed: float, +} + +/* 유형이 선언되면 ReScript는 필요할 때마다 유형을 추론할 수 있습니다. */ +let firstTrip = {destination: "London", capacity: 45, averageSpeed: 120.0} + +/* 점 표기법을 사용하여 속성에 액세스합니다. */ +let maxPassengers = firstTrip.capacity + +/* 레코드 유형을 다른 파일에 정의하는 경우 Trips.res라는 파일에 trainJourney가 있었다면 파일 이름을 참조해야 합니다. */ +let secondTrip: Trips.trainJourney = { + destination: "Paris", + capacity: 50, + averageSpeed: 150.0, +} + +/* 레코드는 기본적으로 불변입니다. */ +/* 그러나 레코드의 내용은 스프레드 연산자를 사용하여 복사할 수 있습니다. */ +let newTrip = {...secondTrip, averageSpeed: 120.0} + +/* 레코드 속성은 `mutable` 키워드로 명시적으로 변경할 수 있습니다. */ +type breakfastCereal = { + name: string, + mutable amount: int, +} + +let tastyMuesli = {name: "Tasty Muesli TM", amount: 500} + +tastyMuesli.amount = 200 +/* - tastyMuesli의 양은 이제 200입니다. */ + +/* Punning은 중복 타이핑을 피하는 데 사용됩니다. */ +let name = "Just As Good Muesli" +let justAsGoodMuesli = {name, amount: 500} +/* - justAsGoodMuesli.name은 이제 "Just As Good Muesli"이며, 이는 + { name: name, amount: 500 }과 동일합니다. */ + +/* > 변형 + 상호 배타적인 상태는 변형으로 표현할 수 있습니다. */ + +type authType = + | GitHub + | Facebook + | Google + | Password +/* - 생성자는 대문자로 시작해야 합니다. */ +/* - 레코드와 마찬가지로 변형은 다른 파일에 선언된 경우 이름을 지정해야 합니다. */ + +let userPreferredAuth = GitHub + +/* 변형은 switch 문과 함께 사용하기에 좋습니다. */ +let loginMessage = + switch (userPreferredAuth) { + | GitHub => "GitHub 자격 증명으로 로그인." + | Facebook => "Facebook 계정으로 로그인." + | Google => "Google 계정으로 로그인" + | Password => "이메일과 비밀번호로 로그인." + } + +/* > 옵션 + 옵션은 None 또는 Some('a)일 수 있으며, 여기서 'a는 유형입니다. */ + +let userId = Some(23) + +/* switch는 두 가지 경우를 처리합니다. */ +let alertMessage = + switch (userId) { + | Some(id) => "환영합니다. 귀하의 ID는" ++ string_of_int(id) + | None => "계정이 없습니다!" + } +/* - `None` 또는 `Some` 케이스가 누락되면 오류가 발생합니다. */ + +/* > 목록 + * 목록은 다음 속성을 가집니다. + - 불변 + - 순서 지정 + - 항목을 앞에 추가하는 속도 + - 분할 속도 + + * ReScript의 목록은 연결 목록입니다. + */ + +/* 목록은 `list` 키워드로 선언되고 중괄호로 묶인 값으로 초기화됩니다. */ +let userIds = list{1, 4, 8} + +/* 유형은 list<'a>로 명시적으로 설정할 수 있으며, 여기서 'a는 유형입니다. */ +type idList = list +type attendanceList = list + +/* 목록은 불변입니다. */ +/* 그러나 기존 목록에 추가된 요소를 사용하여 새 목록을 만들 수 있습니다. */ +let newUserIds = list{101, 102, ...userIds} + +/* > 배열 + * 배열은 다음 속성을 가집니다. + - 변경 가능 + - 임의 접근 및 업데이트 속도 */ + +/* 배열은 `[`로 선언되고 `]`로 끝납니다. */ +let languages = ["ReScript", "JavaScript", "OCaml"] + +/*---------------------------------------------- + * 함수 + *---------------------------------------------- + */ + +/* ReScript 함수는 화살표 구문을 사용하며, 표현식이 반환됩니다. */ +let signUpToNewsletter = email => "Thanks for signing up " ++ email + +/* 다음과 같이 함수를 호출합니다. */ +signUpToNewsletter("hello@ReScript.org") + +/* 더 긴 함수에는 블록을 사용하십시오. */ +let getEmailPrefs = email => { + let message = "Update settings for " ++ email + let prefs = ["Weekly News", "Daily Notifications"] + + (message, prefs) +} +/* - 최종 튜플은 암시적으로 반환됩니다. */ + +/* > 레이블이 지정된 인수 */ + +/* 인수는 ~ 기호로 레이블을 지정할 수 있습니다. */ +let moveTo = (~x, ~y) => { + /* x,y로 이동 */ + () +} + +moveTo(~x=7.0, ~y=3.5) + +/* 레이블이 지정된 인수는 함수 내에서 사용되는 이름을 가질 수도 있습니다. */ +let getMessage = (~message as msg) => "==" ++ msg ++ "==" + +getMessage(~message="You have a message!") +/* - 호출자는 ~message를 지정하지만 내부적으로 함수는 이를 사용할 수 있습니다. */ + +/* 다음 함수도 명시적으로 유형이 선언되어 있습니다. */ +let showDialog = (~message: string): unit => { + () /* 대화 상자 표시 */ +} + +/* > 커링 + 함수는 커링될 수 있으며 부분적으로 호출되어 쉽게 재사용할 수 있습니다. + 나머지 인수는 ...로 표시됩니다. */ + +let div = (denom, numr) => numr / denom +let divBySix = div(6, ...) +let divByTwo = div(2, ...) + +div(3, 24) /* - : int = 8 */ +divBySix(128) /* - : int = 21 */ +divByTwo(10) /* - : int = 5 */ + +/* > 선택적 레이블 인수 */ + +/* 선택적 레이블 인수에 `=?` 구문을 사용하십시오. */ +let greetPerson = (~name, ~greeting=?) => { + switch (greeting) { + | Some(greet) => greet ++ " " ++ name + | None => "Hi " ++ name + } +} +/* - 세 번째 인수 `unit` 또는 `()`는 필수입니다. 이를 생략하면 + 함수가 커링되어 greetPerson(~name="Kate")가 부분 함수를 생성합니다. + 이를 해결하려면 선언 및 호출 시 `unit`을 추가하십시오. */ + +/* 선택적 레이블 인수 없이 greetPerson 호출 */ +greetPerson(~name="Kate") + +/* 모든 인수로 greetPerson 호출 */ +greetPerson(~name="Marco", ~greeting="How are you today,") + +/* > 파이프 */ +/* 함수는 파이프라인 연산자로 호출할 수 있습니다. */ + +/* 첫 번째 인수를 전달하려면 `->`를 사용하십시오(파이프-첫 번째). */ +3->div(24) /* - : int = 8 */ +/* - 이것은 div(3, 24)와 동일합니다. */ + +36->divBySix /* - : int = 6 */ +/* - 이것은 divBySix(36)와 동일합니다. */ + +/* 파이프는 코드를 함께 연결하기 쉽게 만듭니다. */ +let addOne = a => a + 1 +let divByTwo = a => a / 2 +let multByThree = a => a * 3 + +let pipedValue = 3->addOne->divByTwo->multByThree /* - : int = 6 */ + +/*---------------------------------------------- + * 제어 흐름 및 패턴 매칭 + *---------------------------------------------- + */ + +/* > If-else */ +/* ReScript에서 `If`는 결과를 반환하는 표현식입니다. */ + +/* greeting은 "Good morning!"이 됩니다. */ +let greeting = if (true) {"Good morning!"} else {"Hello!"} + +/* else 분기가 없으면 표현식은 `unit` 또는 `()`를 반환합니다. */ +if (false) { + showDialog(~message="정말 떠나시겠습니까?") +} +/* - 결과가 `unit` 유형이므로 결과를 할당하려면 두 반환 유형이 동일해야 합니다. */ + +/* > 구조 분해 */ +/* 데이터 구조에서 속성을 쉽게 추출합니다. */ + +let aTuple = ("Teacher", 101) + +/* 튜플의 값을 추출할 수 있습니다. */ +let (name, classNum) = aTuple + +/* 레코드의 속성도 추출할 수 있습니다. */ +type person = { + firstName: string, + age: int, +} +let bjorn = {firstName: "Bjorn", age: 28} + +/* 변수 이름은 레코드 속성 이름과 일치해야 합니다. */ +let {firstName, age} = bjorn + +/* 그러나 다음과 같이 이름을 바꿀 수 있습니다. */ +let {firstName: bName, age: bAge} = bjorn + +let {firstName: cName, age: _} = bjorn + +/* > Switch + 스위치를 사용한 패턴 매칭은 ReScript의 중요한 도구입니다. + 표현적이고 간결한 도구를 위해 구조 분해와 함께 사용할 수 있습니다. */ + +/* 간단한 목록을 가져옵니다. */ +let firstNames = ["James", "Jean", "Geoff"] + +/* 처리하려는 각 경우에 대해 이름을 패턴 일치시킬 수 있습니다. */ +switch (firstNames) { +| [] => "이름 없음" +| [first] => "오직 " ++ first +| [first, second] => "두 개의 이름 " ++ first ++ "," ++ second +| [first, second, third] => + "세 개의 이름, " ++ first ++ ", " ++ second ++ ", " ++ third +| _ => "많은 이름" +} +/* - `_`는 끝에 있는 모든 것을 포괄하는 것으로, 값에 상관없이 모든 다른 경우와 일치합니다. */ + +/* > When 절 */ + +let isJohn = a => a == "John" +let maybeName = Some("John") + +/* When은 간단한 스위치에 더 복잡한 논리를 추가할 수 있습니다. */ +let aGreeting = + switch (maybeName) { + | Some(name) when isJohn(name) => "안녕하세요 존! 잘 지내세요?" + | Some(name) => "안녕하세요 " ++ name ++ ", 환영합니다." + | None => "인사할 사람이 없습니다." + } + +/* > 예외 */ + +/* 사용자 정의 예외 */ +exception Under_Age + +/* 함수 내에서 예외 발생 */ +let driveToTown = (driver: person) => + if (driver.age >= 15) { + "우리는 마을에 있습니다." + } else { + raise(Under_Age) + } + +let evan = {firstName: "Evan", age: 14} + +/* 패턴 매치 on the exception Under_Age */ +switch (driveToTown(evan)) { +| status => print_endline(status) +| exception Under_Age => + print_endline(evan.firstName ++ "은 운전하기에 너무 어립니다!") +} + +/* 또는 try 블록을 사용할 수 있습니다. */ +/* - ReScript 예외는 옵션으로 피할 수 있으며 거의 사용되지 않습니다. */ +let messageToEvan = + try { + driveToTown(evan) + } catch { + | Under_Age => evan.firstName ++ "은 운전하기에 너무 어립니다!" + } + +/*---------------------------------------------- + * 객체 + *---------------------------------------------- + * 객체는 레코드 유형과 유사하지만 덜 엄격합니다. + * 객체는 클래스와 유사합니다. + */ + +/* 객체는 레코드처럼 유형이 지정될 수 있지만 속성 이름은 인용됩니다. */ +type surfaceComputer = { + "color": string, + "capacity": int, +} +let surfaceBook: surfaceComputer = { "color": "blue", "capacity": 512 } + +/* 객체는 유형이 필요하지 않습니다. */ +let hamster = { "color": "brown", "age": 2 } + +/* 객체 타이핑은 구조적이므로 필요한 필드가 있는 모든 객체를 허용하는 함수를 가질 수 있습니다. */ +let getAge = animal => animal["age"] +getAge(hamster) +getAge({ "name": "Fido", "color": "silver", "age": 3 }) +getAge({ "age": 5 }) + +/*---------------------------------------------- + * 모듈 + *---------------------------------------------- + * 모듈은 코드를 구성하고 네임스페이스를 제공하는 데 사용됩니다. + * 각 파일은 기본적으로 모듈입니다. + */ + +/* 모듈 생성 */ +module Staff = { + type role = + | Delivery + | Sales + | Other + type member = { + name: string, + role, + } + + let getRoleDirectionMessage = staff => + switch (staff.role) { + | Delivery => "진심으로 배달하십시오!" + | Sales => "당신만이 할 수 있는 것처럼 판매하십시오!" + | Other => "당신은 팀의 중요한 부분입니다!" + } +} + +/* 모듈은 점 표기법으로 액세스할 수 있습니다. */ +let newEmployee: Staff.member = {name: "Laura", role: Staff.Delivery} + +/* 모듈 이름을 사용하는 것이 번거로울 수 있으므로 `open`을 사용하여 모듈의 내용을 현재 범위로 열 수 있습니다. */ +open Staff + +let otherNewEmployee: member = {name: "Fred", role: Other} + +/* 모듈은 `include` 키워드를 사용하여 확장할 수 있습니다. include는 새 모듈의 범위에 모듈의 내용을 복사합니다. */ +module SpecializedStaff = { + include Staff + + /* `member`가 포함되어 있으므로 명시적으로 참조할 필요가 없습니다. */ + let ceo: member = {name: "Reggie", role: Other} + + let getMeetingTime = staff => + switch (staff) { + | Other => 11_15 /* - : int = 1115 밑줄은 서식 지정용입니다. */ + | _ => 9_30 + } +} +``` + +## 더 읽을거리 + +- [공식 ReScript 문서](https://rescript-lang.org/) +- [ReScript 사용해 보기 - 온라인 플레이그라운드](https://rescript-lang.org/try) \ No newline at end of file diff --git a/ko/rst.md b/ko/rst.md new file mode 100644 index 0000000000..1cc3df3469 --- /dev/null +++ b/ko/rst.md @@ -0,0 +1,116 @@ +--- +name: reStructuredText (RST) +contributors: + - ["DamienVGN", "https://github.com/martin-damien"] + - ["Andre Polykanine", "https://github.com/Oire"] +filename: restructuredtext.rst +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +RST, 재구조화된 텍스트는 파이썬 커뮤니티에서 문서를 작성하기 위해 만든 파일 형식입니다. [Docutils](https://docutils.sourceforge.io/rst.html)의 일부입니다. + +RST는 HTML과 같은 마크업 언어이지만 훨씬 가볍고 읽기 쉽습니다. + + +## 설치 + +재구조화된 텍스트를 사용하려면 [Python](http://www.python.org)과 `docutils` 패키지를 설치해야 합니다. + +`docutils`는 명령줄을 사용하여 설치할 수 있습니다. + +```bash +$ easy_install docutils +``` + +시스템에 `pip`가 있는 경우에도 사용할 수 있습니다. + +```bash +$ pip install docutils +``` + + +## 파일 구문 + +파일 구문의 간단한 예: + +```rst +.. 두 개의 점으로 시작하는 줄은 특수 명령어입니다. 하지만 명령어를 찾을 수 없으면 해당 줄은 주석으로 간주됩니다. + +========================================================= +주요 제목은 위아래에 등호 기호를 사용하여 작성됩니다. +========================================================= + +공백을 포함한 각 문자 위아래에 등호 기호가 필요하다는 점에 유의하십시오. + +제목도 등호 기호를 사용하지만 아래에만 있습니다. +==================================================== + +대시가 있는 부제목 +--------------------- + +텍스트를 *기울임꼴* 또는 **굵게** 표시할 수 있으며, 이중 백쿼트 ``print()``로 텍스트를 코드로 "표시"할 수 있습니다. + +특수 문자는 백슬래시를 사용하여 이스케이프할 수 있습니다(예: \\ 또는 \*). + +목록은 마크다운과 유사하지만 조금 더 복잡합니다. + +목록 기호(- 또는 \*)를 이전 텍스트 블록의 왼쪽 가장자리에 맞추고, 새 목록을 부모 목록과 분리하기 위해 빈 줄을 사용하는 것을 잊지 마십시오. + +- 첫 번째 항목 +- 두 번째 항목 + + - 하위 항목 + +- 세 번째 항목 + +또는 + +* 첫 번째 항목 +* 두 번째 항목 + + * 하위 항목 + +* 세 번째 항목 + +표는 정말 쉽게 작성할 수 있습니다. + +=========== ======== +국가 수도 +=========== ======== +프랑스 파리 +일본 도쿄 +=========== ======== + +더 복잡한 표(병합된 열 및/또는 행)도 쉽게 만들 수 있지만, 이를 위해서는 전체 문서를 읽는 것이 좋습니다. :) + +링크를 만드는 방법에는 여러 가지가 있습니다. + +- 단어 뒤에 밑줄을 추가하여: GitHub_ 그리고 텍스트 뒤에 대상 URL을 추가하여 (이 방법은 보이는 텍스트에 불필요한 URL을 삽입하지 않는 장점이 있습니다). +- 전체 이해 가능한 URL을 입력하여: https://github.com/ (자동으로 링크로 변환됩니다). +- 더 마크다운과 유사한 링크를 만들어: `GitHub `_ . + +.. _GitHub: https://github.com/ +``` + + +## 사용 방법 + +RST는 docutils와 함께 제공되며, 예를 들어 `rst2html`이 있습니다. + +```bash +$ rst2html myfile.rst output.html +``` + +*참고: 일부 시스템에서는 명령어가 rst2html.py일 수 있습니다.* + +하지만 RST 형식을 사용하는 더 복잡한 응용 프로그램이 있습니다. + +- [Pelican](http://blog.getpelican.com/), 정적 사이트 생성기 +- [Sphinx](http://sphinx-doc.org/), 문서 생성기 +- 그리고 다른 많은 것들 + + +## 읽을거리 + +- [공식 빠른 참조](http://docutils.sourceforge.net/docs/user/rst/quickref.html) diff --git a/ko/ruby-ecosystem.md b/ko/ruby-ecosystem.md new file mode 100644 index 0000000000..03985699ff --- /dev/null +++ b/ko/ruby-ecosystem.md @@ -0,0 +1,128 @@ +--- +category: tool +name: Ruby 생태계 +contributors: + - ["Jon Smock", "http://github.com/jonsmock"] + - ["Rafal Chmiel", "http://github.com/rafalchmiel"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Ruby를 사용하는 사람들은 일반적으로 다른 Ruby 버전을 설치하고, +패키지(또는 젬)를 관리하고, 젬 종속성을 관리하는 방법을 가지고 있습니다. + +## Ruby 버전 + +Ruby는 마츠모토 유키히로("Matz")가 만들었으며, 그는 최근에 바뀌고 있지만 다소 +[BDFL](https://en.wikipedia.org/wiki/Benevolent_Dictator_for_Life)로 남아 있습니다. +결과적으로 Ruby의 참조 구현은 +MRI(Matz' Reference Implementation)라고 하며, Ruby 버전을 들으면 +MRI의 릴리스 버전을 의미합니다. + +새로운 주요 Ruby 버전은 전통적으로 크리스마스에 출시됩니다. 현재 주요 버전(2017년 12월 25일)은 2.5입니다. 가장 인기 있는 안정 버전은 2.4.4와 2.3.7입니다(둘 다 2018년 3월 28일 출시). + +## Ruby 관리자 + +일부 플랫폼에는 Ruby가 사전 설치되어 있거나 패키지로 제공됩니다. 대부분의 루비스트는 +이것들을 사용하지 않거나, 사용하더라도 다른 Ruby +설치 프로그램이나 구현을 부트스트랩하는 데만 사용합니다. 대신 루비스트는 Ruby 관리자를 +설치하여 여러 버전의 Ruby와 프로젝트의 Ruby +환경을 설치하고 전환하는 경향이 있습니다. + +다음은 인기 있는 Ruby 환경 관리자입니다: + +* [RVM](https://rvm.io/) - 루비를 설치하고 전환합니다. RVM은 또한 + 프로젝트의 환경을 완전히 격리하기 위한 젬셋 개념을 가지고 있습니다. +* [ruby-build](https://github.com/sstephenson/ruby-build) - 루비만 설치합니다. + 루비 설치를 더 세밀하게 제어하려면 이것을 사용하십시오. +* [rbenv](https://github.com/sstephenson/rbenv) - 루비 간에만 전환합니다. + ruby-build와 함께 사용됩니다. 루비 로드 방식을 더 세밀하게 제어하려면 이것을 사용하십시오. +* [chruby](https://github.com/postmodern/chruby) - 루비 간에만 전환합니다. + 정신적으로 rbenv와 유사합니다. 루비 설치 방식에 대해 의견이 없습니다. + +## Ruby 구현 + +Ruby 생태계는 각각 고유한 강점과 호환성 상태를 가진 +많은 다른 Ruby 구현을 즐깁니다. 명확히 하자면, 다른 +구현은 다른 언어로 작성되었지만, *모두 Ruby*입니다. +각 구현에는 특별한 후크와 추가 기능이 있지만, 모두 +정상적인 Ruby 파일을 잘 실행합니다. 예를 들어, JRuby는 Java로 작성되었지만, +사용하기 위해 Java를 알 필요는 없습니다. + +매우 성숙/호환 가능: + +* [MRI](https://github.com/ruby/ruby) - C로 작성된 Ruby의 참조 구현입니다. + 정의상 100% 호환됩니다(자체적으로). 다른 모든 루비는 + MRI와 호환성을 유지합니다(아래 [Ruby Spec](#ruby-spec) 참조). +* [JRuby](http://jruby.org/) - Java와 Ruby로 작성된 이 견고한 구현은 매우 빠릅니다. + 가장 중요한 것은 JRuby의 강점은 JVM/Java 상호 운용성이며, 기존 +JVM 도구, 프로젝트 및 언어를 활용합니다. +* [Rubinius](http://rubini.us/) - 주로 Ruby 자체로 작성되었으며 C++ 바이트코드 VM이 있습니다. 또한 + 성숙하고 빠릅니다. Ruby 자체로 구현되었기 때문에 많은 VM +기능을 루비랜드에 노출합니다. + +중간 정도 성숙/호환 가능: + +* [Maglev](http://maglev.github.io/) - Smalltalk VM인 Gemstone 위에 구축되었습니다. Smalltalk에는 몇 가지 + 인상적인 도구가 있으며, 이 프로젝트는 그것을 Ruby +개발에 도입하려고 합니다. +* [RubyMotion](http://www.rubymotion.com/) - Ruby를 iOS 개발에 도입합니다. + +덜 성숙/호환 가능: + +* [Topaz](http://topazruby.com/) - RPython(PyPy 도구 체인 사용)으로 작성된 Topaz는 상당히 젊고 + 아직 호환되지 않습니다. 고성능 Ruby +구현이 될 가능성을 보여줍니다. +* [IronRuby](http://ironruby.net/) - C#으로 작성되어 .NET 플랫폼을 대상으로 하는 IronRuby 작업은 + Microsoft가 지원을 철회한 이후 중단된 것으로 보입니다. + +Ruby 구현은 자체 릴리스 버전 번호를 가질 수 있지만, 항상 +호환성을 위해 특정 버전의 MRI를 대상으로 합니다. 많은 구현에는 +어떤 MRI 버전을 대상으로 할지 지정하기 위해 다른 모드(예: 1.8 또는 1.9 모드)로 +들어가는 기능이 있습니다. + +## Ruby Spec + +대부분의 Ruby 구현은 [Ruby Spec](https://github.com/ruby/spec)에 크게 의존합니다. Ruby +에는 공식 사양이 없으므로 커뮤니티는 +Ruby로 실행 가능한 사양을 작성하여 구현의 MRI와의 +호환성을 테스트합니다. + +## RubyGems + +[RubyGems](http://rubygems.org/)는 Ruby용 커뮤니티 운영 패키지 관리자입니다. +RubyGems는 Ruby와 함께 제공되므로 별도로 다운로드할 필요가 없습니다. + +Ruby 패키지는 "젬"이라고 하며, 커뮤니티에서 +RubyGems.org에 호스팅할 수 있습니다. 각 젬에는 소스 코드와 버전, +종속성, 작성자, 라이선스 등과 같은 일부 메타데이터가 포함되어 있습니다. + +## Bundler + +[Bundler](http://bundler.io/)는 젬 종속성 해결사입니다. 프로젝트의 +Gemfile을 사용하여 종속성을 찾은 다음 해당 종속성의 종속성을 +재귀적으로 가져옵니다. 모든 종속성이 해결되고 다운로드될 때까지 +이 작업을 수행하거나 충돌이 발견되면 중지합니다. + +Bundler는 충돌하는 종속성을 발견하면 오류를 발생시킵니다. 예를 들어, +젬 A가 젬 Z의 버전 3 이상을 요구하지만 젬 B가 버전 2를 요구하는 경우 +Bundler는 충돌을 알려줍니다. 이것은 많은 +젬이 다른 젬을 참조(다른 젬을 참조)하여 해결해야 할 큰 +종속성 그래프를 형성할 수 있으므로 매우 유용합니다. + +# 테스트 + +테스트는 Ruby 문화의 큰 부분입니다. Ruby에는 minitest(Ruby 버전 1.8.x의 경우 TestUnit)라는 +자체 Unit 스타일 테스트 프레임워크가 함께 제공됩니다. +다른 목표를 가진 많은 테스트 라이브러리가 있습니다. + +* [TestUnit](http://ruby-doc.org/stdlib-1.8.7/libdoc/test/unit/rdoc/Test/Unit.html) - Ruby 1.8의 내장 "Unit 스타일" 테스트 프레임워크 +* [minitest](http://ruby-doc.org/stdlib-2.0.0/libdoc/minitest/rdoc/MiniTest.html) - Ruby 1.9/2.0의 내장 테스트 프레임워크 +* [RSpec](http://rspec.info/) - 표현력에 중점을 둔 테스트 프레임워크 +* [Cucumber](http://cukes.info/) - Gherkin 형식 테스트를 구문 분석하는 BDD 테스트 프레임워크 + +## 친절하게 대하기 + +Ruby 커뮤니티는 개방적이고 다양하며 환영하는 커뮤니티라는 자부심을 가지고 있습니다. +Matz 자신은 매우 친절하며, 전체적으로 루비스트의 관대함은 +놀랍습니다. diff --git a/ko/ruby.md b/ko/ruby.md new file mode 100644 index 0000000000..ac5da4bee2 --- /dev/null +++ b/ko/ruby.md @@ -0,0 +1,674 @@ +--- +name: Ruby +filename: learnruby.rb +contributors: + - ["David Underwood", "http://theflyingdeveloper.com"] + - ["Joel Walden", "http://joelwalden.net"] + - ["Luke Holder", "http://twitter.com/lukeholder"] + - ["Tristan Hume", "http://thume.ca/"] + - ["Nick LaMuro", "https://github.com/NickLaMuro"] + - ["Marcos Brizeno", "http://www.about.me/marcosbrizeno"] + - ["Ariel Krakowski", "http://www.learneroo.com"] + - ["Dzianis Dashkevich", "https://github.com/dskecse"] + - ["Levi Bostian", "https://github.com/levibostian"] + - ["Rahil Momin", "https://github.com/iamrahil"] + - ["Gabriel Halley", "https://github.com/ghalley"] + - ["Persa Zula", "http://persazula.com"] + - ["Jake Faris", "https://github.com/farisj"] + - ["Corey Ward", "https://github.com/coreyward"] + - ["Jannik Siebert", "https://github.com/janniks"] + - ["Keith Miyake", "https://github.com/kaymmm"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```ruby +# 이것은 주석입니다 + +=begin +이것은 여러 줄 주석입니다. +시작 줄은 "=begin"으로 시작해야 합니다 +그리고 끝 줄은 "=end"로 시작해야 합니다. + +이렇게 하거나, 여러 줄 주석의 +각 줄을 # 문자로 시작할 수 있습니다. +=end + +# Ruby에서는 (거의) 모든 것이 객체입니다. +# 이것은 숫자도 포함합니다... +3.class #=> Integer + +# ...그리고 문자열도... +"Hello".class #=> String + +# ...심지어 메서드도! +"Hello".method(:class).class #=> Method + +# 몇 가지 기본 산술 +1 + 1 #=> 2 +8 - 1 #=> 7 +10 * 2 #=> 20 +35 / 5 #=> 7 +2 ** 5 #=> 32 +5 % 3 #=> 2 + +# 비트 연산자 +3 & 5 #=> 1 +3 | 5 #=> 7 +3 ^ 5 #=> 6 + +# 산술은 단지 문법적 설탕일 뿐입니다 +# 객체에 대한 메서드 호출을 위한 +1.+(3) #=> 4 +10.* 5 #=> 50 +100.methods.include?(:/) #=> true + +# 특수 값은 객체입니다 +nil # 다른 언어의 null과 동일 +true # 참 +false # 거짓 + +nil.class #=> NilClass +true.class #=> TrueClass +false.class #=> FalseClass + +# 동등성 +1 == 1 #=> true +2 == 1 #=> false + +# 부등성 +1 != 1 #=> false +2 != 1 #=> true + +# false 자체를 제외하고, nil은 유일한 다른 '거짓' 값입니다 + +!!nil #=> false +!!false #=> false +!!0 #=> true +!!"" #=> true + +# 더 많은 비교 +1 < 10 #=> true +1 > 10 #=> false +2 <= 2 #=> true +2 >= 2 #=> true + +# 결합 비교 연산자 (첫 번째 인수가 더 크면 `1`을, +# 두 번째 인수가 더 크면 `-1`을, 그렇지 않으면 `0`을 반환) +1 <=> 10 #=> -1 (1 < 10) +10 <=> 1 #=> 1 (10 > 1) +1 <=> 1 #=> 0 (1 == 1) + +# 논리 연산자 +true && false #=> false +true || false #=> true + +# 훨씬 낮은 우선순위를 가진 논리 연산자의 대체 버전이 있습니다. +# 이것들은 그들 중 하나가 참 또는 거짓을 반환할 때까지 문장을 +# 함께 연결하는 흐름 제어 구문으로 사용하기 위한 것입니다. + +# `do_something_else`는 `do_something`이 성공하는 경우에만 호출됩니다. +do_something() and do_something_else() +# `log_error`는 `do_something`이 실패하는 경우에만 호출됩니다. +do_something() or log_error() + +# 문자열 보간 + +placeholder = '문자열 보간 사용' +"큰따옴표 문자열을 사용할 때 #{placeholder}할 수 있습니다" +#=> "큰따옴표 문자열을 사용할 때 문자열 보간 사용 할 수 있습니다" + +# `+`를 사용하여 문자열을 결합할 수 있지만, 다른 유형과는 결합할 수 없습니다 +'hello ' + 'world' #=> "hello world" +'hello ' + 3 #=> TypeError: no implicit conversion of Integer into String +'hello ' + 3.to_s #=> "hello 3" +"hello #{3}" #=> "hello 3" + +# ...또는 문자열과 연산자 결합 +'hello ' * 3 #=> "hello hello hello " + +# ...또는 문자열에 추가 +'hello' << ' world' #=> "hello world" + +# 끝에 개행 문자를 사용하여 출력에 인쇄할 수 있습니다 +puts "인쇄 중입니다!" +#=> 인쇄 중입니다! +#=> nil + +# ...또는 개행 문자 없이 출력에 인쇄 +print "인쇄 중입니다!" +#=> "인쇄 중입니다!" => nil + +# 변수 +x = 25 #=> 25 +x #=> 25 + +# 할당은 할당된 값을 반환한다는 점에 유의하십시오. +# 이것은 다중 할당을 할 수 있음을 의미합니다. + +x = y = 10 #=> 10 +x #=> 10 +y #=> 10 + +# 관례적으로, 변수 이름에는 snake_case를 사용합니다. +snake_case = true + +# 설명적인 변수 이름 사용 +path_to_project_root = '/good/name/' +m = '/bad/name/' + +# 심볼은 정수 값으로 내부적으로 표현되는 불변의 재사용 가능한 상수입니다. +# 특정 의미 있는 값을 효율적으로 전달하기 위해 문자열 대신 자주 사용됩니다. + +:pending.class #=> Symbol + +status = :pending + +status == :pending #=> true + +status == 'pending' #=> false + +status == :approved #=> false + +# 문자열은 심볼로, 심볼은 문자열로 변환할 수 있습니다. +status.to_s #=> "pending" +"argon".to_sym #=> :argon + +# 배열 + +# 이것은 배열입니다. +array = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5] + +# 배열은 다른 유형의 항목을 포함할 수 있습니다. +[1, 'hello', false] #=> [1, "hello", false] + +# 따옴표 대신 %w를 선호할 수 있습니다 +%w[foo bar baz] #=> ["foo", "bar", "baz"] + +# 배열은 인덱싱할 수 있습니다. +# 앞에서부터... +array[0] #=> 1 +array.first #=> 1 +array[12] #=> nil + +# ...또는 뒤에서부터... +array[-1] #=> 5 +array.last #=> 5 + +# ...또는 시작 인덱스와 길이로... +array[2, 3] #=> [3, 4, 5] + +# ...또는 범위로... +array[1..3] #=> [2, 3, 4] + +# 배열을 뒤집을 수 있습니다. +# 뒤집힌 값으로 새 배열 반환 +[1,2,3].reverse #=> [3,2,1] +# 변수를 뒤집힌 값으로 업데이트하기 위해 배열을 제자리에서 뒤집기 +a = [1,2,3] +a.reverse! #=> a==[3,2,1] bang ('!') 호출 때문에 + +# 산술과 마찬가지로 [var] 액세스는 단지 문법적 설탕일 뿐입니다 +# 객체에서 '[]' 메서드를 호출하기 위한 +array.[] 0 #=> 1 +array.[] 12 #=> nil + +# 배열에 추가할 수 있습니다... +array << 6 #=> [1, 2, 3, 4, 5, 6] +# 또는 이렇게 +array.push(6) #=> [1, 2, 3, 4, 5, 6] + +# ...그리고 항목이 배열에 있는지 확인 +array.include?(1) #=> true + +# 해시는 키/값 쌍이 있는 Ruby의 기본 사전입니다. +# 해시는 중괄호로 표시됩니다. +hash = { 'color' => 'green', 'number' => 5 } + +hash.keys #=> ['color', 'number'] + +# 해시는 키로 빠르게 조회할 수 있습니다. +hash['color'] #=> "green" +hash['number'] #=> 5 + +# 존재하지 않는 키에 대해 해시를 요청하면 nil이 반환됩니다. +hash['nothing here'] #=> nil + +# 해시에서 키에 심볼을 사용할 때 대체 구문을 사용할 수 있습니다. + +hash = { :defcon => 3, :action => true } +hash.keys #=> [:defcon, :action] + +hash = { defcon: 3, action: true } +hash.keys #=> [:defcon, :action] + +# 해시에서 키와 값의 존재 확인 +hash.key?(:defcon) #=> true +hash.value?(3) #=> true + +# 팁: 배열과 해시는 모두 Enumerable입니다! +# each, map, count 등과 같은 많은 유용한 메서드를 공유합니다. + +# 제어 구조 + +# 조건문 +if true + 'if 문' +elsif false + 'else if, 선택 사항' +else + 'else, 또한 선택 사항' +end + +# 조건이 코드 블록이 아닌 단일 문장의 호출을 제어하는 경우 +# 후위-if 표기법을 사용할 수 있습니다. +warnings = ['Patronimic is missing', 'Address too short'] +puts("Some warnings occurred:\n" + warnings.join("\n")) if !warnings.empty? + +# `if`보다 `unless`가 더 잘 들리면 조건을 재구성하십시오. +puts("Some warnings occurred:\n" + warnings.join("\n")) unless warnings.empty? + +# 루프 +# Ruby에서는 전통적인 `for` 루프가 그다지 일반적이지 않습니다. 대신, 이러한 +# 기본 루프는 `each`에 의존하는 enumerable을 사용하여 구현됩니다. +(1..5).each do |counter| + puts "iteration #{counter}" +end + +# 이것은 대략 다음과 같으며 Ruby에서는 보기 드문 경우입니다. +for counter in 1..5 + puts "iteration #{counter}" +end + +# 위의 `do |variable| ... end` 구문은 '블록'이라고 합니다. 블록은 +# 다른 프로그래밍 언어의 람다, 익명 함수 또는 클로저와 유사합니다. +# 객체로 전달하거나, 호출하거나, 메서드로 첨부할 수 있습니다. +# +# 범위의 'each' 메서드는 범위의 각 요소에 대해 블록을 한 번 실행합니다. +# 블록은 매개변수로 카운터를 전달받습니다. + +# 중괄호로 블록을 둘러쌀 수도 있습니다. +(1..5).each { |counter| puts "iteration #{counter}" } + +# 데이터 구조의 내용은 each를 사용하여 반복할 수도 있습니다. +array.each do |element| + puts "#{element} is part of the array" +end +hash.each do |key, value| + puts "#{key} is #{value}" +end + +# 여전히 인덱스가 필요한 경우 'each_with_index'를 사용하고 인덱스 +# 변수를 정의할 수 있습니다. +array.each_with_index do |element, index| + puts "#{element} is number #{index} in the array" +end + +counter = 1 +while counter <= 5 do + puts "iteration #{counter}" + counter += 1 +end +#=> iteration 1 +#=> iteration 2 +#=> iteration 3 +#=> iteration 4 +#=> iteration 5 + +# Ruby에는 다른 유용한 반복 함수가 많이 있습니다. +# 예를 들어: 'map', 'reduce', 'inject' 등 목록이 계속됩니다. +# Map은 예를 들어 반복하는 배열을 가져와 +# 블록에 정의된 대로 무언가를 수행하고 완전히 새로운 배열을 반환합니다. +array = [1,2,3,4,5] +doubled = array.map do |element| + element * 2 +end +puts doubled +#=> [2,4,6,8,10] +puts array +#=> [1,2,3,4,5] + +# 또 다른 유용한 구문은 .map(&:method)입니다. +a = ["FOO", "BAR", "BAZ"] +a.map { |s| s.downcase } #=> ["foo", "bar", "baz"] +a.map(&:downcase) #=> ["foo", "bar", "baz"] + +# Case 구문 +grade = 'B' + +case grade +when 'A' + puts 'Way to go kiddo' +when 'B' + puts 'Better luck next time' +when 'C' + puts 'You can do better' +when 'D' + puts 'Scraping through' +when 'F' + puts 'You failed!' +else + puts 'Alternative grading system, eh?' +end +#=> "Better luck next time" + +# Case는 범위를 사용할 수도 있습니다. +grade = 82 +case grade +when 90..100 + puts 'Hooray!' +when 80...90 + puts 'OK job' +else + puts 'You failed!' +end +#=> "OK job" + +# 예외 처리 +begin + # 예외를 발생시킬 수 있는 코드 + raise NoMemoryError, 'You ran out of memory.' +rescue NoMemoryError => exception_variable + puts 'NoMemoryError was raised', exception_variable +rescue RuntimeError => other_exception_variable + puts 'RuntimeError was raised now' +else + puts 'This runs if no exceptions were thrown at all' +ensure + puts 'This code always runs no matter what' +end + +# 메서드 + +def double(x) + x * 2 +end + +# 메서드(및 블록)는 마지막 문의 값을 암시적으로 반환합니다. +double(2) #=> 4 + +# 해석이 명확한 경우 괄호는 선택 사항입니다. +double 3 #=> 6 + +double double 3 #=> 12 + +def sum(x, y) + x + y +end + +# 메서드 인수는 쉼표로 구분됩니다. +sum 3, 4 #=> 7 + +sum sum(3, 4), 5 #=> 12 + +# yield +# 모든 메서드에는 암시적이고 선택적인 블록 매개변수가 있습니다. +# 'yield' 키워드로 호출할 수 있습니다. +def surround + puts '{' + yield + puts '}' +end + +surround { puts 'hello world' } + +#=> { +#=> hello world +#=> } + +# 블록은 'proc' 객체로 변환될 수 있으며, 이는 블록을 래핑하고 +# 다른 메서드에 전달하거나, 다른 범위에 바인딩하거나, +# 다른 방식으로 조작할 수 있도록 합니다. 이것은 메서드 매개변수 목록에서 +# 가장 일반적이며, 블록이 주어지면 블록을 수락하고 +# 'Proc'으로 변환하는 후행 '&block' 매개변수를 자주 볼 수 있습니다. +# 여기서 이름 지정은 관례이며, '&pineapple'과도 잘 작동합니다. +def guests(&block) + block.class #=> Proc + block.call(4) +end + +# Proc의 'call' 메서드는 블록이 있을 때 'yield'를 호출하는 것과 유사합니다. +# 'call'에 전달된 인수는 블록에 인수로 전달됩니다. + +guests { |n| "You have #{n} guests." } +# => "You have 4 guests." + +# 배열로 변환될 인수 목록을 전달할 수 있습니다. +# 그것이 스플랫 연산자("*")의 용도입니다. +def guests(*array) + array.each { |guest| puts guest } +end + +# 약식 블록 구문도 있습니다. 모든 배열 항목에 대해 간단한 +# 메서드를 호출해야 할 때 가장 유용합니다. +upcased = ['Watch', 'these', 'words', 'get', 'upcased'].map(&:upcase) +puts upcased +#=> ["WATCH", "THESE", "WORDS", "GET", "UPCASED"] + +sum = [1, 2, 3, 4, 5].reduce(&:+) +puts sum +#=> 15 + +# 구조 분해 + +# Ruby는 여러 변수에 할당할 때 배열을 자동으로 구조 분해합니다. +a, b, c = [1, 2, 3] +a #=> 1 +b #=> 2 +c #=> 3 + +# 어떤 경우에는 스플랫 연산자: `*`를 사용하여 배열을 +# 목록으로 구조 분해하라는 메시지를 표시해야 합니다. +ranked_competitors = ["John", "Sally", "Dingus", "Moe", "Marcy"] + +def best(first, second, third) + puts "Winners are #{first}, #{second}, and #{third}." +end + +best *ranked_competitors.first(3) #=> Winners are John, Sally, and Dingus. + +# 스플랫 연산자는 매개변수에서도 사용할 수 있습니다. +def best(first, second, third, *others) + puts "Winners are #{first}, #{second}, and #{third}." + puts "There were #{others.count} other participants." +end + +best *ranked_competitors +#=> Winners are John, Sally, and Dingus. +#=> There were 2 other participants. + +# 관례적으로, 부울을 반환하는 모든 메서드는 물음표로 끝납니다. +5.even? #=> false +5.odd? #=> true + +# 관례적으로, 메서드 이름이 느낌표로 끝나면 수신자를 +# 변경하는 것과 같은 파괴적인 작업을 수행합니다. 많은 메서드에는 +# 변경을 위한 ! 버전과 변경된 새 버전을 반환하는 +# 비-! 버전이 있습니다. +company_name = "Dunder Mifflin" +company_name.upcase #=> "DUNDER MIFFLIN" +company_name #=> "Dunder Mifflin" +# 이번에는 company_name을 변경합니다. +company_name.upcase! #=> "DUNDER MIFFLIN" +company_name #=> "DUNDER MIFFLIN" + +# 클래스 + +# 'class' 키워드로 클래스를 정의할 수 있습니다. +class Human + + # 클래스 변수. 이 클래스의 모든 인스턴스에서 공유됩니다. + @@species = 'H. sapiens' + + # 기본 초기화자 + def initialize(name, age = 0) + # 인수를 인스턴스의 'name' 인스턴스 변수에 할당합니다. + @name = name + # 나이가 주어지지 않으면 인수 목록의 기본값으로 돌아갑니다. + @age = age + end + + # 기본 설정자 메서드 + def name=(name) + @name = name + end + + # 기본 접근자 메서드 + def name + @name + end + + # 위의 기능은 다음과 같이 attr_accessor 메서드를 사용하여 캡슐화할 수 있습니다. + attr_accessor :name + + # 접근자/설정자 메서드는 다음과 같이 개별적으로 만들 수도 있습니다. + attr_reader :name + attr_writer :name + + # 클래스 메서드는 인스턴스 메서드와 구별하기 위해 self를 사용합니다. + # 클래스에서만 호출할 수 있으며 인스턴스에서는 호출할 수 없습니다. + def self.say(msg) + puts msg + end + + def species + @@species + end +end + +# 클래스 인스턴스화 +jim = Human.new('Jim Halpert') +dwight = Human.new('Dwight K. Schrute') + +# 생성된 객체의 메서드를 호출할 수 있습니다. +jim.species #=> "H. sapiens" +jim.name #=> "Jim Halpert" +jim.name = "Jim Halpert II" #=> "Jim Halpert II" +jim.name #=> "Jim Halpert II" +dwight.species #=> "H. sapiens" +dwight.name #=> "Dwight K. Schrute" + +# 클래스 메서드 호출 +Human.say('Hi') #=> "Hi" + +# 변수의 범위는 이름을 지정하는 방식으로 정의됩니다. +# $로 시작하는 변수는 전역 범위를 가집니다. +$var = "I'm a global var" +defined? $var #=> "global-variable" + +# @로 시작하는 변수는 인스턴스 범위를 가집니다. +@var = "I'm an instance var" +defined? @var #=> "instance-variable" + +# @@로 시작하는 변수는 클래스 범위를 가집니다. +@@var = "I'm a class var" +defined? @@var #=> "class variable" + +# 대문자로 시작하는 변수는 상수입니다. +Var = "I'm a constant" +defined? Var #=> "constant" + +# 클래스는 ruby에서 객체이기도 합니다. 따라서 클래스는 인스턴스 변수를 가질 수 있습니다. +# 클래스 변수는 클래스와 모든 하위 클래스에서 공유됩니다. + +# 기본 클래스 +class Human + @@foo = 0 + + def self.foo + @@foo + end + + def self.foo=(value) + @@foo = value + end +end + +# 파생 클래스 +class Worker < Human +end + +Human.foo #=> 0 +Worker.foo #=> 0 + +Human.foo = 2 +Worker.foo #=> 2 + +# 클래스 인스턴스 변수는 클래스의 하위 클래스에서 공유되지 않습니다. +class Human + @bar = 0 + + def self.bar + @bar + end + + def self.bar=(value) + @bar = value + end +end + +class Doctor < Human +end + +Human.bar #=> 0 +Doctor.bar #=> nil + +module ModuleExample + def foo + 'foo' + end +end + +# 모듈을 포함하면 해당 메서드가 클래스 인스턴스에 바인딩됩니다. +# 모듈을 확장하면 해당 메서드가 클래스 자체에 바인딩됩니다. +class Person + include ModuleExample +end + +class Book + extend ModuleExample +end + +Person.foo #=> NoMethodError: undefined method `foo' for Person:Class +Person.new.foo #=> "foo" +Book.foo #=> "foo" +Book.new.foo #=> NoMethodError: undefined method `foo' + +# 모듈을 포함하고 확장할 때 콜백이 실행됩니다. +module ConcernExample + def self.included(base) + base.extend(ClassMethods) + base.send(:include, InstanceMethods) + end + + module ClassMethods + def bar + 'bar' + end + end + + module InstanceMethods + def qux + 'qux' + end + end +end + +class Something + include ConcernExample +end + +Something.bar #=> "bar" +Something.qux #=> NoMethodError: undefined method `qux' +Something.new.bar #=> NoMethodError: undefined method `bar' +Something.new.qux #=> "qux" +``` + +## 추가 자료 + +- [Ruby 대화형 튜토리얼](https://rubymonk.com/) - 일련의 대화형 튜토리얼을 통해 Ruby를 배우십시오. +- [공식 문서](http://ruby-doc.org/core) +- [다른 언어에서 온 Ruby](https://www.ruby-lang.org/en/documentation/ruby-from-other-languages/) +- [Programming Ruby](http://www.amazon.com/Programming-Ruby-1-9-2-0-Programmers/dp/1937785491/) - 이전 [무료 버전](http://ruby-doc.com/docs/ProgrammingRuby/)은 온라인에서 사용할 수 있습니다. +- [Ruby 스타일 가이드](https://github.com/bbatsov/ruby-style-guide) - 커뮤니티 기반 Ruby 코딩 스타일 가이드. +- [Try Ruby](https://try.ruby-lang.org/) - 브라우저에서 대화형으로 Ruby 프로그래밍 언어의 기본을 배우십시오. diff --git a/ko/rust.md b/ko/rust.md new file mode 100644 index 0000000000..f814494bcd --- /dev/null +++ b/ko/rust.md @@ -0,0 +1,348 @@ +--- +name: Rust +contributors: + - ["P1start", "http://p1start.github.io/"] +filename: learnrust.rs +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Rust는 Mozilla Research에서 개발한 프로그래밍 언어입니다. Rust는 성능에 대한 저수준 제어와 고수준의 편의성 및 메모리의 안전을 보장합니다. + +가비지 수집기나 런타임 없이 이러한 목표를 달성하므로 Rust 라이브러리를 C의 "드롭인 대체품"으로 사용할 수 있습니다. + +Rust의 첫 번째 릴리스인 0.1은 2012년 1월에 있었고, 3년 동안 개발이 너무 빠르게 진행되어 최근까지 안정적인 릴리스 사용이 권장되지 않았으며 대신 야간 빌드를 사용하라는 일반적인 조언이 있었습니다. + +2015년 5월 15일, Rust 1.0이 완전한 하위 호환성 보장과 함께 릴리스되었습니다. 컴파일 시간 개선 및 컴파일러의 다른 측면에 대한 +개선 사항은 현재 야간 빌드에서 사용할 수 있습니다. Rust는 6주마다 정기적으로 릴리스되는 기차 기반 릴리스 모델을 채택했습니다. Rust 1.1 베타는 Rust 1.0 릴리스와 동시에 제공되었습니다. + +Rust는 비교적 저수준 언어이지만, 일반적으로 고수준 언어에서 발견되는 일부 함수형 개념을 가지고 있습니다. 이로 인해 Rust는 빠를 뿐만 아니라 코딩하기 쉽고 효율적입니다. + +```rust +// 이것은 주석입니다. 한 줄 주석은 이렇게 생겼습니다... +// 그리고 이렇게 여러 줄로 확장됩니다. + +/* 블록 주석 + /* 중첩될 수 있습니다. */ */ + +/// 문서 주석은 이렇게 생겼으며 마크다운 표기법을 지원합니다. +/// # 예제 +/// +/// ``` +/// let five = 5 +/// ``` + +/////////////// +// 1. 기본 // +/////////////// + +#[allow(dead_code)] +// 함수 +// `i32`는 32비트 부호 있는 정수 타입입니다. +fn add2(x: i32, y: i32) -> i32 { + // 암시적 반환 (세미콜론 없음) + x + y +} + +#[allow(unused_variables)] +#[allow(unused_assignments)] +#[allow(dead_code)] +// 메인 함수 +fn main() { + // 숫자 // + + // 불변 바인딩 + let x: i32 = 1; + + // 정수/부동 소수점 접미사 + let y: i32 = 13i32; + let f: f64 = 1.3f64; + + // 타입 추론 + // 대부분의 경우 Rust 컴파일러는 변수의 타입을 추론할 수 있으므로 + // 명시적인 타입 주석을 작성할 필요가 없습니다. + // 이 튜토리얼 전체에서 타입은 많은 곳에서 명시적으로 주석 처리되지만, + // 이는 시연 목적으로만 사용됩니다. 타입 추론은 대부분의 경우 + // 이를 처리할 수 있습니다. + let implicit_x = 1; + let implicit_f = 1.3; + + // 산술 + let sum = x + y + 13; + + // 가변 변수 + let mut mutable = 1; + mutable = 4; + mutable += 2; + + // 문자열 // + + // 문자열 리터럴 + let x: &str = "hello world!"; + + // 출력 + println!("{} {}", f, x); // 1.3 hello world! + + // `String` – 힙에 할당된 문자열 + // `Vec`로 저장되며 항상 유효한 UTF-8 시퀀스를 포함하며, + // null로 종료되지 않습니다. + let s: String = "hello world".to_string(); + + // 문자열 슬라이스 – 다른 문자열에 대한 불변 뷰 + // 이것은 기본적으로 문자열의 불변 포인터와 길이입니다. + // 실제로 문자열의 내용을 포함하지 않고, 문자열 버퍼의 시작에 대한 + // 포인터와 길이만 포함합니다. + // 정적으로 할당되거나 다른 객체에 포함됩니다(이 경우 `s`). + // 문자열 슬라이스는 `Vec`에 대한 `&[u8]` 뷰와 같습니다. + let s_slice: &str = &s; + + println!("{} {}", s, s_slice); // hello world hello world + + // 벡터/배열 // + + // 고정 크기 배열 + let four_ints: [i32; 4] = [1, 2, 3, 4]; + + // 동적 배열 (벡터) + let mut vector: Vec = vec![1, 2, 3, 4]; + vector.push(5); + + // 슬라이스 – 벡터 또는 배열에 대한 불변 뷰 + // 이것은 문자열 슬라이스와 매우 유사하지만 벡터용입니다. + let slice: &[i32] = &vector; + + // 디버그 스타일로 무언가를 출력하려면 `{:?}`를 사용합니다. + println!("{:?} {:?}", vector, slice); // [1, 2, 3, 4, 5] [1, 2, 3, 4, 5] + + // 튜플 // + + // 튜플은 잠재적으로 다른 타입의 값의 고정 크기 집합입니다. + let x: (i32, &str, f64) = (1, "hello", 3.4); + + // `let` 구조 분해 + let (a, b, c) = x; + println!("{} {} {}", a, b, c); // 1 hello 3.4 + + // 인덱싱 + println!("{}", x.1); // hello + + ////////////// + // 2. 타입 // + ////////////// + + // 구조체 + struct Point { + x: i32, + y: i32, + } + + let origin: Point = Point { x: 0, y: 0 }; + + // 이름 없는 필드가 있는 구조체, '튜플 구조체'라고 함 + struct Point2(i32, i32); + + let origin2 = Point2(0, 0); + + // 기본 C와 유사한 열거형 + enum Direction { + Left, + Right, + Up, + Down, + } + + let up = Direction::Up; + + // 필드가 있는 열거형 + // 무언가를 선택적으로 만들고 싶다면 표준 라이브러리에 + // `Option`이 있습니다. + enum OptionalI32 { + AnI32(i32), + Nothing, + } + + let two: OptionalI32 = OptionalI32::AnI32(2); + let nothing = OptionalI32::Nothing; + + // 제네릭 // + + struct Foo { bar: T } + + // 이것은 표준 라이브러리에서 `Option`으로 정의됩니다. + // `Option`은 일반적으로 null 포인터가 사용되는 + // 곳에 사용됩니다. + enum Optional { + SomeVal(T), + NoVal, + } + + // 메서드 // + + impl Foo { + // 메서드는 명시적인 `self` 매개변수를 받습니다. + fn bar(&self) -> &T { // self는 빌려옵니다. + &self.bar + } + fn bar_mut(&mut self) -> &mut T { // self는 가변적으로 빌려옵니다. + &mut self.bar + } + fn into_bar(self) -> T { // 여기서 self는 소비됩니다. + self.bar + } + } + + let a_foo = Foo { bar: 1 }; + println!("{}", a_foo.bar()); // 1 + + // 트레이트 (다른 언어에서는 인터페이스 또는 타입 클래스로 알려짐) // + + trait Frobnicate { + fn frobnicate(self) -> Option; + } + + impl Frobnicate for Foo { + fn frobnicate(self) -> Option { + Some(self.bar) + } + } + + let another_foo = Foo { bar: 1 }; + println!("{:?}", another_foo.frobnicate()); // Some(1) + + // 함수 포인터 타입 // + + fn fibonacci(n: u32) -> u32 { + match n { + 0 => 1, + 1 => 1, + _ => fibonacci(n - 1) + fibonacci(n - 2), + } + } + + type FunctionPointer = fn(u32) -> u32; + + let fib : FunctionPointer = fibonacci; + println!("Fib: {}", fib(4)); // 5 + + ///////////////////////// + // 3. 패턴 매칭 // + ///////////////////////// + + let foo = OptionalI32::AnI32(1); + match foo { + OptionalI32::AnI32(n) => println!("it’s an i32: {}", n), + OptionalI32::Nothing => println!("it’s nothing!"), + } + + // 고급 패턴 매칭 + struct FooBar { x: i32, y: OptionalI32 } + let bar = FooBar { x: 15, y: OptionalI32::AnI32(32) }; + + match bar { + FooBar { x: 0, y: OptionalI32::AnI32(0) } => + println!("The numbers are zero!"), + FooBar { x: n, y: OptionalI32::AnI32(m) } if n == m => + println!("The numbers are the same"), + FooBar { x: n, y: OptionalI32::AnI32(m) } => + println!("Different numbers: {} {}", n, m), + FooBar { x: _, y: OptionalI32::Nothing } => + println!("The second number is Nothing!"), + } + + ///////////////////// + // 4. 제어 흐름 // + ///////////////////// + + // `for` 루프/반복 + let array = [1, 2, 3]; + for i in array { + println!("{}", i); + } + + // 범위 + for i in 0u32..10 { + print!("{} ", i); + } + println!(""); + // `0 1 2 3 4 5 6 7 8 9 ` 출력 + + // `if` + if 1 == 1 { + println!("Maths is working!"); + } else { + println!("Oh no..."); + } + + // 표현식으로서의 `if` + let value = if true { + "good" + } else { + "bad" + }; + + // `while` 루프 + while 1 == 1 { + println!("The universe is operating normally."); + // break 문은 while 루프를 빠져나갑니다. + // 쓸모없는 반복을 피합니다. + break + } + + // 무한 루프 + loop { + println!("Hello!"); + // break 문은 루프를 빠져나갑니다. + break + } + + ///////////////////////////////// + // 5. 메모리 안전성 및 포인터 // + ///////////////////////////////// + + // 소유된 포인터 – 한 번에 한 가지만 이 포인터를 '소유'할 수 있습니다. + // 이것은 `Box`가 범위를 벗어날 때 자동으로 안전하게 + // 할당 해제됨을 의미합니다. + let mut mine: Box = Box::new(3); + *mine = 5; // 역참조 + // 여기서 `now_its_mine`은 `mine`의 소유권을 가져옵니다. 즉, `mine`은 이동됩니다. + let mut now_its_mine = mine; + *now_its_mine += 2; + + println!("{}", now_its_mine); // 7 + // println!("{}", mine); // `now_its_mine`이 이제 포인터를 소유하므로 컴파일되지 않습니다. + + // 참조 – 다른 데이터를 참조하는 불변 포인터 + // 값에 대한 참조가 취해지면 해당 값은 '빌려왔다'고 말합니다. + // 값이 불변으로 빌려온 동안에는 변경하거나 이동할 수 없습니다. + // 빌림은 빌리는 변수의 마지막 사용까지 활성화됩니다. + let mut var = 4; + var = 3; + let ref_var: &i32 = &var; + + println!("{}", var); // `mine`과 달리 `var`는 여전히 사용할 수 있습니다. + println!("{}", *ref_var); + // var = 5; // `var`가 빌려왔기 때문에 컴파일되지 않습니다. + // *ref_var = 6; // `ref_var`가 불변 참조이므로 이것도 컴파일되지 않습니다. + ref_var; // 아무 작업도 하지 않지만 사용으로 간주되어 빌림을 활성 상태로 유지합니다. + var = 2; // ref_var는 위 줄 이후 더 이상 사용되지 않으므로 빌림이 종료되었습니다. + + // 가변 참조 + // 값이 가변적으로 빌려온 동안에는 전혀 액세스할 수 없습니다. + let mut var2 = 4; + let ref_var2: &mut i32 = &mut var2; + *ref_var2 += 2; // '*'는 가변적으로 빌려온 var2를 가리키는 데 사용됩니다. + + println!("{}", *ref_var2); // 6 , // var2는 컴파일되지 않습니다. + // ref_var2는 &mut i32 타입이므로 값이 아닌 i32에 대한 참조를 저장합니다. + // var2 = 2; // `var2`가 빌려왔기 때문에 컴파일되지 않습니다. + ref_var2; // 아무 작업도 하지 않지만 사용으로 간주되어 여기까지 빌림을 활성 상태로 유지합니다. +} +``` + +## 더 읽을거리 + +Rust와 그 기호/키워드에 대한 더 깊지만 여전히 빠른 설명을 보려면 Fasterthanlime의 [Rust를 배우는 데 30분](https://fasterthanli.me/articles/a-half-hour-to-learn-rust) 기사가 명확하고 간결한 방식으로 (거의) 모든 것을 설명합니다! + +Rust에는 더 많은 것이 있습니다. 이것은 Rust의 기본 사항일 뿐이므로 가장 중요한 것을 이해할 수 있습니다. Rust에 대해 더 자세히 알아보려면 [The Rust Programming Language](http://doc.rust-lang.org/book/index.html)를 읽고 [/r/rust](http://reddit.com/r/rust) 서브레딧을 확인하십시오. irc.mozilla.org의 #rust 채널 사람들도 항상 신규 사용자를 돕고 싶어합니다. + +공식 [Rust Playground](https://play.rust-lang.org) 또는 주요 [Rust 웹사이트](http://rust-lang.org)에서 온라인 컴파일러로 Rust의 기능을 시험해 볼 수도 있습니다. diff --git a/ko/sass.md b/ko/sass.md new file mode 100644 index 0000000000..7e614e9a0b --- /dev/null +++ b/ko/sass.md @@ -0,0 +1,585 @@ +--- +name: Sass +filename: learnsass.scss +contributors: + - ["Laura Kyle", "https://github.com/LauraNK"] + - ["Sean Corrales", "https://github.com/droidenator"] + - ["Kyle Mendes", "https://github.com/pink401k"] + - ["Keith Miyake", "https://github.com/kaymmm"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Sass는 변수, 중첩, 믹스인 등과 같은 기능을 추가하는 CSS 확장 언어입니다. +Sass(및 [Less](http://lesscss.org/)와 같은 다른 전처리기)는 개발자가 유지 관리 가능하고 DRY(반복하지 않기) 코드를 작성하는 데 도움이 됩니다. + +Sass에는 선택할 수 있는 두 가지 다른 구문 옵션이 있습니다. SCSS는 CSS와 동일한 구문을 가지고 있지만 Sass의 추가 기능이 있습니다. 또는 Sass(원래 구문)는 중괄호와 세미콜론 대신 들여쓰기를 사용합니다. +이 튜토리얼은 SCSS를 사용하여 작성되었습니다. + +이미 CSS3에 익숙하다면 Sass를 비교적 빨리 배울 수 있습니다. 새로운 스타일링 속성을 제공하는 것이 아니라 CSS를 보다 효율적으로 작성하고 유지 관리를 훨씬 쉽게 만드는 도구를 제공합니다. + +```scss +//한 줄 주석은 Sass가 CSS로 컴파일될 때 제거됩니다. + +/* 여러 줄 주석은 유지됩니다. */ + + + +/* 변수 +============================== */ + + + +/* CSS 값(예: 색상)을 변수에 저장할 수 있습니다. +'$' 기호를 사용하여 변수를 만듭니다. */ + +$primary-color: #A3A4FF; +$secondary-color: #51527F; +$body-font: 'Roboto', sans-serif; + +/* 스타일시트 전체에서 변수를 사용할 수 있습니다. +이제 색상을 변경하려면 한 번만 변경하면 됩니다. */ + +body { + background-color: $primary-color; + color: $secondary-color; + font-family: $body-font; +} + +/* 이것은 다음과 같이 컴파일됩니다: */ +body { + background-color: #A3A4FF; + color: #51527F; + font-family: 'Roboto', sans-serif; +} + +/* 이것은 스타일시트 전체에서 색상이 나타날 때마다 +변경해야 하는 것보다 훨씬 유지 관리가 용이합니다. */ + + + +/* 제어 지시문 +============================== */ + +/* Sass는 @if, @else, @for, @while 및 @each를 사용하여 + 코드를 CSS로 컴파일하는 것을 제어할 수 있습니다. */ + +/* @if/@else 블록은 예상대로 정확하게 작동합니다. */ + +$debug: true !default; + +@mixin debugmode { + @if $debug { + @debug "디버그 모드 활성화됨"; + + display: inline-block; + } + @else { + display: none; + } +} + +.info { + @include debugmode; +} + +/* $debug가 true로 설정되면 .info가 표시되고, false로 설정되면 +.info가 표시되지 않습니다. + +참고: @debug는 명령줄에 디버깅 정보를 출력합니다. +SCSS를 디버깅하는 동안 변수를 확인하는 데 유용합니다. */ + +.info { + display: inline-block; +} + +/* @for는 값 범위를 반복하는 제어 루프입니다. +항목 모음에 스타일을 설정하는 데 특히 유용합니다. +"through"와 "to"의 두 가지 형식이 있습니다. 전자는 마지막 값을 포함하고, +후자는 마지막 값에서 멈춥니다. */ + +@for $c from 1 to 4 { + div:nth-of-type(#{$c}) { + left: ($c - 1) * 900 / 3; + } +} + +@for $c from 1 through 3 { + .myclass-#{$c} { + color: rgb($c * 255 / 3, $c * 255 / 3, $c * 255 / 3); + } +} + +/* 다음과 같이 컴파일됩니다: */ + +div:nth-of-type(1) { + left: 0; +} + +div:nth-of-type(2) { + left: 300; +} + +div:nth-of-type(3) { + left: 600; +} + +.myclass-1 { + color: #555555; +} + +.myclass-2 { + color: #aaaaaa; +} + +.myclass-3 { + color: white; +// SASS는 #FFFFFF를 자동으로 white로 변환합니다. +} + +/* @while은 매우 간단합니다: */ + +$columns: 4; +$column-width: 80px; + +@while $columns > 0 { + .col-#{$columns} { + width: $column-width; + left: $column-width * ($columns - 1); + } + + $columns: $columns - 1; +} + +/* 다음 CSS를 출력합니다: */ + +.col-4 { + width: 80px; + left: 240px; +} + +.col-3 { + width: 80px; + left: 160px; +} + +.col-2 { + width: 80px; + left: 80px; +} + +.col-1 { + width: 80px; + left: 0px; +} + +/* @each는 서수 값 대신 목록을 사용하는 것을 제외하고 @for와 같이 작동합니다. +참고: 다른 변수와 마찬가지로 목록을 지정하며 공백을 +구분 기호로 사용합니다. */ + +$social-links: facebook twitter linkedin reddit; + +.social-links { + @each $sm in $social-links { + .icon-#{$sm} { + background-image: url("images/#{$sm}.png"); + } + } +} + +/* 다음과 같이 출력됩니다: */ + +.social-links .icon-facebook { + background-image: url("images/facebook.png"); +} + +.social-links .icon-twitter { + background-image: url("images/twitter.png"); +} + +.social-links .icon-linkedin { + background-image: url("images/linkedin.png"); +} + +.social-links .icon-reddit { + background-image: url("images/reddit.png"); +} + + +/* 믹스인 +==============================*/ + +/* 둘 이상의 요소에 대해 동일한 코드를 작성하는 경우 +해당 코드를 믹스인에 저장할 수 있습니다. + +'@mixin' 지시문과 믹스인 이름을 사용하십시오. */ + +@mixin center { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; +} + +/* '@include'와 믹스인 이름을 사용하여 믹스인을 사용할 수 있습니다. */ + +div { + @include center; + background-color: $primary-color; +} + +/* 다음과 같이 컴파일됩니다: */ +div { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + background-color: #A3A4FF; +} + +/* 믹스인을 사용하여 약식 속성을 만들 수 있습니다. */ + +@mixin size($width, $height) { + width: $width; + height: $height; +} + +/* 너비와 높이 인수를 전달하여 호출할 수 있습니다. */ + +.rectangle { + @include size(100px, 60px); +} + +.square { + @include size(40px, 40px); +} + +/* 다음과 같이 컴파일됩니다: */ +.rectangle { + width: 100px; + height: 60px; +} + +.square { + width: 40px; + height: 40px; +} + + + +/* 함수 +============================== */ + + + +/* Sass는 다양한 작업을 수행하는 데 사용할 수 있는 함수를 제공합니다. + 다음을 고려하십시오. */ + +/* 함수는 이름을 사용하고 필요한 인수를 전달하여 호출할 수 있습니다. */ +body { + width: round(10.25px); +} + +.footer { + background-color: fade_out(#000000, 0.25); +} + +/* 다음과 같이 컴파일됩니다: */ + +body { + width: 10px; +} + +.footer { + background-color: rgba(0, 0, 0, 0.75); +} + +/* 자신만의 함수를 정의할 수도 있습니다. 함수는 믹스인과 매우 유사합니다. + 함수와 믹스인 중에서 선택할 때 믹스인은 + CSS를 생성하는 데 가장 적합하고 함수는 Sass 코드 전체에서 + 사용될 수 있는 논리에 더 적합하다는 것을 기억하십시오. + '수학 연산자' 섹션의 예는 재사용 가능한 + 함수가 되기에 이상적인 후보입니다. */ + +/* 이 함수는 대상 크기와 부모 크기를 가져와 + 백분율을 계산하고 반환합니다. */ + +@function calculate-percentage($target-size, $parent-size) { + @return $target-size / $parent-size * 100%; +} + +$main-content: calculate-percentage(600px, 960px); + +.main-content { + width: $main-content; +} + +.sidebar { + width: calculate-percentage(300px, 960px); +} + +/* 다음과 같이 컴파일됩니다: */ + +.main-content { + width: 62.5%; +} + +.sidebar { + width: 31.25%; +} + + + +/* 확장 (상속) +============================== */ + + + +/* 확장은 한 선택자의 속성을 다른 선택자와 공유하는 방법입니다. */ + +.display { + @include size(5em, 5em); + border: 5px solid $secondary-color; +} + +.display-success { + @extend .display; + border-color: #22df56; +} + +/* 다음과 같이 컴파일됩니다: */ +.display, .display-success { + width: 5em; + height: 5em; + border: 5px solid #51527F; +} + +.display-success { + border-color: #22df56; +} + +/* CSS 문을 확장하는 것이 믹스인을 만드는 것보다 바람직합니다. + Sass가 동일한 기본 스타일을 공유하는 모든 클래스를 + 그룹화하는 방식 때문입니다. 믹스인으로 수행했다면 + 너비, 높이 및 테두리가 믹스인을 호출한 각 문에 대해 + 복제됩니다. 작업 흐름에 영향을 미치지는 않지만 + Sass 컴파일러가 만든 파일에 불필요한 팽창을 추가합니다. */ + + + +/* 중첩 +============================== */ + + + +/* Sass는 선택자 내에 선택자를 중첩할 수 있습니다. */ + +ul { + list-style-type: none; + margin-top: 2em; + + li { + background-color: #FF0000; + } +} + +/* '&'는 부모 선택자로 대체됩니다. */ +/* 의사 클래스를 중첩할 수도 있습니다. */ +/* 과도한 중첩은 코드를 덜 유지 관리하기 쉽게 만든다는 점을 명심하십시오. +모범 사례는 중첩할 때 3단계 이상 깊이 들어가지 않는 것을 권장합니다. +예를 들어: */ + +ul { + list-style-type: none; + margin-top: 2em; + + li { + background-color: red; + + &:hover { + background-color: blue; + } + + a { + color: white; + } + } +} + +/* 다음과 같이 컴파일됩니다: */ + +ul { + list-style-type: none; + margin-top: 2em; +} + +ul li { + background-color: red; +} + +ul li:hover { + background-color: blue; +} + +ul li a { + color: white; +} + + + +/* 부분 파일 및 가져오기 +============================== */ + + + +/* Sass는 부분 파일을 만들 수 있습니다. 이렇게 하면 Sass + 코드를 모듈화하는 데 도움이 될 수 있습니다. 부분 파일은 '_'로 시작해야 합니다(예: _reset.css). + 부분 파일은 CSS로 생성되지 않습니다. */ + +/* _reset.css라는 파일에 넣을 다음 CSS를 고려하십시오. */ + +html, +body, +ul, +ol { + margin: 0; + padding: 0; +} + +/* Sass는 부분 파일을 파일로 가져오는 데 사용할 수 있는 @import를 제공합니다. + 이것은 가져온 파일을 가져오기 위해 다른 HTTP 요청을 하는 + 전통적인 CSS @import 문과 다릅니다. Sass는 가져온 + 파일을 가져와 컴파일된 코드와 결합합니다. */ + +@import 'reset'; + +body { + font-size: 16px; + font-family: Helvetica, Arial, Sans-serif; +} + +/* 다음과 같이 컴파일됩니다: */ + +html, body, ul, ol { + margin: 0; + padding: 0; +} + +body { + font-size: 16px; + font-family: Helvetica, Arial, Sans-serif; +} + + + +/* 자리 표시자 선택자 +============================== */ + + + +/* 자리 표시자는 확장할 CSS 문을 만들 때 유용합니다. + @extend와 함께 독점적으로 사용되는 CSS 문을 만들고 싶다면 + 자리 표시자를 사용하여 그렇게 할 수 있습니다. 자리 표시자는 '.' 또는 '#' 대신 + '%'로 시작합니다. 자리 표시자는 컴파일된 CSS에 나타나지 않습니다. */ + +%content-window { + font-size: 14px; + padding: 10px; + color: #000; + border-radius: 4px; +} + +.message-window { + @extend %content-window; + background-color: #0000ff; +} + +/* 다음과 같이 컴파일됩니다: */ + +.message-window { + font-size: 14px; + padding: 10px; + color: #000; + border-radius: 4px; +} + +.message-window { + background-color: #0000ff; +} + + + +/* 수학 연산 +============================== */ + + + +/* Sass는 +, -, *, /, % 연산자를 제공합니다. 이것들은 + 직접 계산한 값을 사용하는 대신 Sass 파일에서 직접 + 값을 계산하는 데 유용할 수 있습니다. 아래는 간단한 + 2열 디자인을 설정하는 예입니다. */ + +$content-area: 960px; +$main-content: 600px; +$sidebar-content: 300px; + +$main-size: $main-content / $content-area * 100%; +$sidebar-size: $sidebar-content / $content-area * 100%; +$gutter: 100% - ($main-size + $sidebar-size); + +body { + width: 100%; +} + +.main-content { + width: $main-size; +} + +.sidebar { + width: $sidebar-size; +} + +.gutter { + width: $gutter; +} + +/* 다음과 같이 컴파일됩니다: */ + +body { + width: 100%; +} + +.main-content { + width: 62.5%; +} + +.sidebar { + width: 31.25%; +} + +.gutter { + width: 6.25%; +} +``` + +## SASS인가, Sass인가? +Sass가 약어인지 궁금한 적이 있습니까? 아마 없을 것입니다. 하지만 어쨌든 알려 드리겠습니다. 언어의 이름은 "Sass"라는 단어이며 약어가 아닙니다. +사람들이 계속 "SASS"라고 썼기 때문에 언어 제작자는 농담으로 "구문적으로 멋진 스타일시트(Syntactically Awesome StyleSheets)"라고 불렀습니다. + + +## Sass 연습 +브라우저에서 Sass를 가지고 놀고 싶다면 [SassMeister](http://sassmeister.com/)를 확인하십시오. +두 구문 중 하나를 사용할 수 있으며, 설정으로 이동하여 Sass 또는 SCSS를 선택하기만 하면 됩니다. + + +## 호환성 +Sass는 CSS로 컴파일하는 프로그램이 있는 한 모든 프로젝트에서 사용할 수 있습니다. 사용 중인 CSS가 대상 브라우저와 호환되는지 확인해야 합니다. + +[QuirksMode CSS](http://www.quirksmode.org/css/) 및 [CanIUse](http://caniuse.com)는 호환성을 확인하는 데 훌륭한 리소스입니다. + + +## 더 읽을거리 +* [공식 문서](http://sass-lang.com/documentation/file.SASS_REFERENCE.html) +* [The Sass Way](http://thesassway.com/)는 튜토리얼(초급-고급)과 기사를 제공합니다. diff --git a/ko/scala.md b/ko/scala.md new file mode 100644 index 0000000000..90270dd0c0 --- /dev/null +++ b/ko/scala.md @@ -0,0 +1,743 @@ +--- +name: Scala +filename: learnscala.scala +contributors: + - ["George Petrov", "http://github.com/petrovg"] + - ["Dominic Bou-Samra", "http://dbousamra.github.com"] + - ["Geoff Liu", "http://geoffliu.me"] + - ["Ha-Duong Nguyen", "http://reference-error.org"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- +Scala - 확장 가능한 언어 + +```scala +///////////////////////////////////////////////// +// 0. 기본 +///////////////////////////////////////////////// +/* + Scala 설정: + + 1) Scala 다운로드 - http://www.scala-lang.org/downloads + 2) 원하는 위치에 압축을 풀고 bin 하위 디렉토리를 `PATH` 환경 변수에 추가 +*/ + +/* + REPL 사용해보기 + + Scala에는 REPL(Read-Eval-Print Loop)이라는 도구가 있으며, 이는 다른 많은 언어의 + 명령줄 인터프리터와 유사합니다. 모든 Scala 표현식을 입력하면 + 결과가 평가되어 출력됩니다. + + REPL은 코드를 테스트하고 확인하는 데 매우 유용한 도구입니다. 이 튜토리얼을 + 읽으면서 사용하여 개념을 직접 빠르게 탐색하십시오. +*/ + +// `scala`를 실행하여 Scala REPL을 시작합니다. 다음과 같은 프롬프트를 볼 수 있습니다: +$ scala +scala> + +// 기본적으로 입력하는 각 표현식은 새로운 번호가 매겨진 값으로 저장됩니다 +scala> 2 + 2 +res0: Int = 4 + +// 기본값을 재사용할 수 있습니다. 결과에 표시되는 값 유형에 유의하십시오.. +scala> res0 + 2 +res1: Int = 6 + +// Scala는 강력한 타입 언어입니다. REPL을 사용하여 표현식을 평가하지 않고 +// 타입을 확인할 수 있습니다. +scala> :type (true, 2.0) +(Boolean, Double) + +// REPL 세션을 저장할 수 있습니다 +scala> :save /sites/repl-test.scala + +// 파일을 REPL로 로드할 수 있습니다 +scala> :load /sites/repl-test.scala +Loading /sites/repl-test.scala... +res2: Int = 4 +res3: Int = 6 + +// 최근 기록을 검색할 수 있습니다 +scala> :h? +1 2 + 2 +2 res0 + 2 +3 :save /sites/repl-test.scala +4 :load /sites/repl-test.scala +5 :h? + +// 이제 어떻게 사용하는지 알았으니, 스칼라를 조금 배워봅시다... + +///////////////////////////////////////////////// +// 1. 기본 +///////////////////////////////////////////////// + +// 한 줄 주석은 두 개의 슬래시로 시작합니다 + +/* + 위에서 이미 볼 수 있듯이 여러 줄 주석은 이렇습니다. +*/ + +// 출력하고, 다음 출력에서 새 줄을 강제합니다 +println("Hello world!") +println(10) +// Hello world! +// 10 + +// 출력하고, 다음 출력에서 새 줄을 강제하지 않습니다 +print("Hello world") +print(10) +// Hello world10 + +// 값 선언은 var 또는 val을 사용하여 수행됩니다. +// val 선언은 불변인 반면, var는 가변입니다. 불변성은 +// 좋은 것입니다. +val x = 10 // x는 이제 10입니다 +x = 20 // 오류: val에 재할당 +var y = 10 +y = 20 // y는 이제 20입니다 + +/* + Scala는 정적 타입 언어이지만, 위 선언에서 + 타입을 지정하지 않았다는 점에 유의하십시오. 이는 타입 추론이라는 + 언어 기능 때문입니다. 대부분의 경우 Scala 컴파일러는 변수의 + 타입을 추측할 수 있으므로 매번 입력할 필요가 없습니다. + 다음과 같이 변수의 타입을 명시적으로 선언할 수 있습니다: +*/ +val z: Int = 10 +val a: Double = 1.0 + +// Int에서 Double로의 자동 변환에 유의하십시오. 결과는 10이 아닌 10.0입니다. +val b: Double = 10 + +// 불리언 값 +true +false + +// 불리언 연산 +!true // false +!false // true +true == false // false +10 > 5 // true + +// 수학은 평소와 같습니다 +1 + 1 // 2 +2 - 1 // 1 +5 * 3 // 15 +6 / 2 // 3 +6 / 4 // 1 +6.0 / 4 // 1.5 +6 / 4.0 // 1.5 + + +// REPL에서 표현식을 평가하면 결과의 타입과 값이 제공됩니다 + +1 + 7 + +/* 위 줄의 결과는 다음과 같습니다: + + scala> 1 + 7 + res29: Int = 8 + + 이는 1 + 7을 평가한 결과가 값이 8인 Int 타입의 객체임을 의미합니다. + + "res29"는 입력한 표현식의 결과를 저장하기 위해 순차적으로 생성된 + 변수 이름이며, 출력은 다를 수 있습니다. +*/ + +"Scala 문자열은 큰따옴표로 묶입니다" +'a' // Scala Char +// '작은따옴표 문자열은 존재하지 않습니다' <= 이로 인해 오류가 발생합니다 + +// 문자열에는 일반적인 Java 메서드가 정의되어 있습니다 +"hello world".length +"hello world".substring(2, 6) +"hello world".replace("C", "3") + +// 추가 Scala 메서드도 있습니다. scala.collection.immutable.StringOps도 참조하십시오. +"hello world".take(5) +"hello world".drop(5) + +// 문자열 보간: 접두사 "s"에 유의하십시오 +val n = 45 +s"We have $n apples" // => "We have 45 apples" + +// 보간된 문자열 내의 표현식도 가능합니다 +val a = Array(11, 9, 6) +s"My second daughter is ${a(0) - a(2)} years old." // => "My second daughter is 5 years old." +s"We have double the amount of ${n / 2.0} in apples." // => "We have double the amount of 22.5 in apples." +s"Power of 2: ${math.pow(2, 2)}" // => "Power of 2: 4" + +// 접두사 "f"를 사용하여 보간된 문자열로 서식 지정 +f"Power of 5: ${math.pow(5, 2)}%1.0f" // "Power of 5: 25" +f"Square root of 122: ${math.sqrt(122)}%1.4f" // "Square root of 122: 11.0454" + +// 원시 문자열, 특수 문자 무시. +raw"New line feed: \n. Carriage return: \r." // => "New line feed: \n. Carriage return: \r." + +// 일부 문자는 "이스케이프"해야 합니다. 예를 들어 문자열 내의 큰따옴표: +"They stood outside the \"Rose and Crown\"" // => "They stood outside the "Rose and Crown"" + +// 세 개의 큰따옴표는 문자열이 여러 줄에 걸쳐 있고 따옴표를 포함할 수 있도록 합니다 +val html = """ +

Press belo', Joe

+ + """ + + +///////////////////////////////////////////////// +// 2. 함수 +///////////////////////////////////////////////// + +// 함수는 다음과 같이 정의됩니다: +// +// def functionName(args...): ReturnType = { body... } +// +// 더 전통적인 언어에서 온 경우 return 키워드의 생략에 유의하십시오. +// Scala에서는 함수 블록의 마지막 표현식이 반환 값입니다. +def sumOfSquares(x: Int, y: Int): Int = { + val x2 = x * x + val y2 = y * y + x2 + y2 +} + +// 함수 본문이 단일 표현식인 경우 { }를 생략할 수 있습니다: +def sumOfSquaresShort(x: Int, y: Int): Int = x * x + y * y + +// 함수 호출 구문은 익숙합니다: +sumOfSquares(3, 4) // => 25 + +// 매개변수 이름을 사용하여 다른 순서로 지정할 수 있습니다 +def subtract(x: Int, y: Int): Int = x - y + +subtract(10, 3) // => 7 +subtract(y=10, x=3) // => -7 + +// 대부분의 경우(재귀 함수가 가장 주목할 만한 예외) 함수 +// 반환 타입을 생략할 수 있으며, 변수에서 보았던 것과 동일한 타입 추론이 +// 함수 반환 값에서도 작동합니다: +def sq(x: Int) = x * x // 컴파일러는 반환 타입이 Int라고 추측할 수 있습니다 + +// 함수에는 기본 매개변수가 있을 수 있습니다: +def addWithDefault(x: Int, y: Int = 5) = x + y +addWithDefault(1, 2) // => 3 +addWithDefault(1) // => 6 + + +// 익명 함수는 다음과 같습니다: +(x: Int) => x * x + +// def와 달리, 컨텍스트가 명확하면 익명 함수의 입력 타입도 생략할 수 있습니다. +// "Int => Int" 타입은 Int를 받아 Int를 반환하는 함수를 의미합니다. +val sq: Int => Int = x => x * x + +// 익명 함수는 평소와 같이 호출할 수 있습니다: +sq(10) // => 100 + +// 익명 함수의 각 인수가 한 번만 사용되는 경우, +// Scala는 더 짧은 방법으로 정의할 수 있습니다. 이러한 +// 익명 함수는 데이터 구조 섹션에서 명백해지겠지만 매우 일반적입니다. +val addOne: Int => Int = _ + 1 +val weirdSum: (Int, Int) => Int = (_ * 2 + _ * 3) + +addOne(5) // => 6 +weirdSum(2, 4) // => 16 + + +// return 키워드는 Scala에 존재하지만, 가장 안쪽의 +// def에서만 반환합니다. +// 경고: Scala에서 return을 사용하는 것은 오류가 발생하기 쉬우므로 피해야 합니다. +// 익명 함수에는 영향을 미치지 않습니다. 예를 들어 여기서 foo(7)이 17을 반환할 것으로 예상할 수 있지만 7을 반환합니다: +def foo(x: Int): Int = { + val anonFunc: Int => Int = { z => + if (z > 5) + return z // 이 줄은 z를 foo의 반환 값으로 만듭니다! + else + z + 2 // 이 줄은 anonFunc의 반환 값입니다 + } + anonFunc(x) + 10 // 이 줄은 foo의 반환 값입니다 +} + +foo(7) // => 7 + +///////////////////////////////////////////////// +// 3. 제어 흐름 +///////////////////////////////////////////////// + +1 to 5 +val r = 1 to 5 +r.foreach(println) + +r foreach println +// 참고: Scala는 점과 괄호에 대해 상당히 관대합니다 - +// 규칙을 별도로 연구하십시오. 이는 영어처럼 읽히는 DSL 및 API를 작성하는 데 도움이 됩니다. + +// 왜 여기서 `println`에 매개변수가 필요하지 않을까요? +// 아래 함수형 프로그래밍 섹션의 일급 함수를 기대하십시오! +(5 to 1 by -1) foreach (println) + +// while 루프 +var i = 0 +while (i < 10) { println("i " + i); i += 1 } + +while (i < 10) { println("i " + i); i += 1 } // 네, 다시. 무슨 일이 있었나요? 왜요? + +i // i의 값을 표시합니다. while은 고전적인 의미의 루프입니다 - + // 루프 변수를 변경하면서 순차적으로 실행됩니다. while은 매우 + // 빠르지만, 위의 조합기 및 내포를 사용하는 것이 이해하기 쉽고 + // 병렬화하기 쉽습니다. + +// do-while 루프 +i = 0 +do { + println("i is still less than 10") + i += 1 +} while (i < 10) + +// 재귀는 Scala에서 (대부분의 다른 함수형 언어에서와 같이) +// 작업을 반복하는 관용적인 방법입니다. +// 재귀 함수는 명시적인 반환 타입이 필요하며, 컴파일러는 이를 추론할 수 없습니다. +// 여기서 Unit은 Java의 `void` 반환 타입과 유사합니다. +def showNumbersInRange(a: Int, b: Int): Unit = { + print(a) + if (a < b) + showNumbersInRange(a + 1, b) +} +showNumbersInRange(1, 14) + + +// 조건문 + +val x = 10 + +if (x == 1) println("yeah") +if (x == 10) println("yeah") +if (x == 11) println("yeah") +if (x == 11) println("yeah") else println("nay") + +println(if (x == 10) "yeah" else "nope") +val text = if (x == 10) "yeah" else "nope" + + +///////////////////////////////////////////////// +// 4. 데이터 구조 +///////////////////////////////////////////////// + +val a = Array(1, 2, 3, 5, 8, 13) +a(0) // Int = 1 +a(3) // Int = 5 +a(21) // 예외 발생 + +val m = Map("fork" -> "tenedor", "spoon" -> "cuchara", "knife" -> "cuchillo") +m("fork") // java.lang.String = tenedor +m("spoon") // java.lang.String = cuchara +m("bottle") // 예외 발생 + +val safeM = m.withDefaultValue("no lo se") +safeM("bottle") // java.lang.String = no lo se + +val s = Set(1, 3, 7) +s(0) // Boolean = false +s(1) // Boolean = true + +/* 여기서 맵 설명서를 찾아보십시오 - + * https://www.scala-lang.org/api/current/scala/collection/immutable/Map.html + * 그리고 읽을 수 있는지 확인하십시오. + */ + + +// 튜플 + +(1, 2) + +(4, 3, 2) + +(1, 2, "three") + +(a, 2, "three") + +// 왜 이것이 있을까요? +val divideInts = (x: Int, y: Int) => (x / y, x % y) + +// divideInts 함수는 결과와 나머지를 제공합니다 +divideInts(10, 3) // (Int, Int) = (3,1) + +// 튜플의 요소에 액세스하려면 _._n을 사용하십시오. 여기서 n은 요소의 1-기반 인덱스입니다. +val d = divideInts(10, 3) // (Int, Int) = (3,1) + +d._1 // Int = 3 +d._2 // Int = 1 + +// 또는 튜플에 다중 변수 할당을 할 수 있으며, 이는 많은 경우에 더 편리하고 +// 읽기 쉽습니다. +val (div, mod) = divideInts(10, 3) + +div // Int = 3 +mod // Int = 1 + + +///////////////////////////////////////////////// +// 5. 객체 지향 프로그래밍 +///////////////////////////////////////////////// + +/* + 참고: 이 튜토리얼에서 지금까지 수행한 모든 작업은 간단한 + 표현식(값, 함수 등)이었습니다. 이러한 표현식은 빠른 테스트를 위해 + 명령줄 인터프리터에 입력하기에 좋지만, Scala 파일에 + 단독으로 존재할 수는 없습니다. 예를 들어 Scala 파일에 "val x = 5"만 있을 수는 없습니다. + 대신 Scala에서 허용되는 유일한 최상위 구문은 다음과 같습니다: + + - objects + - classes + - case classes + - traits + + 이제 이것들이 무엇인지 설명하겠습니다. +*/ + +// 클래스는 다른 언어의 클래스와 유사합니다. 생성자 인수는 +// 클래스 이름 뒤에 선언되고, 초기화는 클래스 본문에서 수행됩니다. +class Dog(br: String) { + // 생성자 코드 여기 + var breed: String = br + + // bark라는 메서드를 정의하고 String을 반환합니다 + def bark = "Woof, woof!" + + // 값과 메서드는 public으로 간주됩니다. "protected" 및 "private" 키워드도 + // 사용할 수 있습니다. + private def sleep(hours: Int) = + println(s"I'm sleeping for $hours hours") + + // 추상 메서드는 단순히 본문이 없는 메서드입니다. 아래 def 줄의 주석을 + // 해제하면 Dog 클래스를 다음과 같이 abstract로 선언해야 합니다: + // abstract class Dog(...) { ... } + // def chaseAfter(what: String): String +} + +val mydog = new Dog("greyhound") +println(mydog.breed) // => "greyhound" +println(mydog.bark) // => "Woof, woof!" + + +// "object" 키워드는 타입과 해당 타입의 싱글턴 인스턴스를 생성합니다. +// Scala 클래스가 "컴패니언 객체"를 갖는 것이 일반적이며, 여기서 인스턴스별 +// 동작은 클래스 자체에 캡처되지만, 해당 클래스의 모든 +// 인스턴스와 관련된 동작은 객체로 이동합니다. 차이점은 다른 언어의 +// 클래스 메서드와 정적 메서드와 유사합니다. 객체와 클래스는 +// 동일한 이름을 가질 수 있습니다. +object Dog { + def allKnownBreeds = List("pitbull", "shepherd", "retriever") + def createDog(breed: String) = new Dog(breed) +} + + +// 케이스 클래스는 추가 기능이 내장된 클래스입니다. Scala 초보자를 위한 +// 일반적인 질문은 언제 클래스를 사용하고 언제 케이스 클래스를 사용해야 하는지입니다. +// 경계가 모호하지만, 일반적으로 클래스는 캡슐화, 다형성 및 +// 동작에 중점을 둡니다. 이러한 클래스의 값은 private인 경향이 있으며, +// 메서드만 노출됩니다. 케이스 클래스의 주요 목적은 +// 불변 데이터를 보유하는 것입니다. 메서드가 거의 없으며, +// 메서드에는 부작용이 거의 없습니다. +case class Person(name: String, phoneNumber: String) + +// 새 인스턴스를 만듭니다. 케이스 클래스는 "new"가 필요하지 않습니다. +val george = Person("George", "1234") +val kate = Person("Kate", "4567") + +// 케이스 클래스를 사용하면 다음과 같은 몇 가지 특전을 무료로 얻을 수 있습니다. +george.phoneNumber // => "1234" + +// 필드별 동등성 (.equals를 재정의할 필요 없음) +Person("George", "1234") == Person("Kate", "1236") // => false + +// 쉬운 복사 방법 +// otherGeorge == Person("George", "9876") +val otherGeorge = george.copy(phoneNumber = "9876") + +// 그리고 다른 많은 것들. 케이스 클래스는 패턴 매칭도 무료로 얻습니다. 아래 참조. + +// 트레이트 +// Java 인터페이스와 유사하게, 트레이트는 객체 타입과 메서드 +// 시그니처를 정의합니다. Scala는 이러한 메서드의 부분 구현을 허용합니다. +// 생성자 매개변수는 허용되지 않습니다. 트레이트는 매개변수 없이 다른 +// 트레이트나 클래스에서 상속할 수 있습니다. + +trait Dog { + def breed: String + def color: String + def bark: Boolean = true + def bite: Boolean +} +class SaintBernard extends Dog { + val breed = "Saint Bernard" + val color = "brown" + def bite = false +} + +scala> val b = new SaintBernard +res0: SaintBernard = SaintBernard@3e57cd70 +scala> b.breed +res1: String = Saint Bernard +scala> b.bark +res2: Boolean = true +scala> b.bite +res3: Boolean = false + +// 트레이트는 믹스인으로도 사용할 수 있습니다. 클래스는 첫 번째 트레이트를 "extends"하지만, +// "with" 키워드는 추가 트레이트를 추가할 수 있습니다. + +trait Bark { + def bark: String = "Woof" +} +trait Dog { + def breed: String + def color: String +} +class SaintBernard extends Dog with Bark { + val breed = "Saint Bernard" + val color = "brown" +} + +scala> val b = new SaintBernard +b: SaintBernard = SaintBernard@7b69c6ba +scala> b.bark +res0: String = Woof + + +///////////////////////////////////////////////// +// 6. 패턴 매칭 +///////////////////////////////////////////////// + +// 패턴 매칭은 Scala에서 강력하고 일반적으로 사용되는 기능입니다. 다음은 +// 케이스 클래스를 패턴 매칭하는 방법입니다. 참고: 다른 언어와 달리 Scala 케이스는 +// break가 필요 없으며, fall-through가 발생하지 않습니다. + +def matchPerson(person: Person): String = person match { + // 그런 다음 패턴을 지정합니다: + case Person("George", number) => "We found George! His number is " + number + case Person("Kate", number) => "We found Kate! Her number is " + number + case Person(name, number) => "We matched someone : " + name + ", phone : " + number +} + +// 정규식도 내장되어 있습니다. +// 문자열에서 `r` 메서드를 사용하여 정규식을 만듭니다: +val email = "(.*)@(.*)".r + +// 패턴 매칭은 C 계열 언어의 switch 문과 비슷해 보일 수 있지만, +// 훨씬 더 강력합니다. Scala에서는 훨씬 더 많은 것을 매칭할 수 있습니다: +def matchEverything(obj: Any): String = obj match { + // 값을 매칭할 수 있습니다: + case "Hello world" => "Got the string Hello world" + + // 타입별로 매칭할 수 있습니다: + case x: Double => "Got a Double: " + x + + // 조건을 지정할 수 있습니다: + case x: Int if x > 10000 => "Got a pretty big number!" + + // 이전과 같이 케이스 클래스를 매칭할 수 있습니다: + case Person(name, number) => s"Got contact info for $name!" + + // 정규식을 매칭할 수 있습니다: + case email(name, domain) => s"Got email address $name@$domain" + + // 튜플을 매칭할 수 있습니다: + case (a: Int, b: Double, c: String) => s"Got a tuple: $a, $b, $c" + + // 데이터 구조를 매칭할 수 있습니다: + case List(1, b, c) => s"Got a list with three elements and starts with 1: 1, $b, $c" + + // 패턴을 중첩할 수 있습니다: + case List(List((1, 2, "YAY"))) => "Got a list of list of tuple" + + // 이전 모든 것이 일치하지 않으면 모든 경우를 매칭합니다 (기본값) + case _ => "Got unknown object" +} + +// 사실, "unapply" 메서드가 있는 모든 객체를 패턴 매칭할 수 있습니다. 이 +// 기능은 너무 강력해서 Scala는 전체 함수를 패턴으로 정의할 수 있습니다: +val patternFunc: Person => String = { + case Person("George", number) => s"George's number: $number" + case Person(name, number) => s"Random person's number: $number" +} + + +///////////////////////////////////////////////// +// 7. 함수형 프로그래밍 +///////////////////////////////////////////////// + +// Scala는 메서드와 함수가 다른 함수나 메서드를 반환하거나 +// 매개변수로 받을 수 있도록 허용합니다. + +val add10: Int => Int = _ + 10 // Int를 받아 Int를 반환하는 함수 +List(1, 2, 3) map add10 // List(11, 12, 13) - add10이 각 요소에 적용됩니다 + +// 명명된 함수 대신 익명 함수를 사용할 수 있습니다: +List(1, 2, 3) map (x => x + 10) + +// 그리고 익명 함수에 인수가 하나만 있는 경우 밑줄 기호를 사용할 수 있습니다. +// 변수로 바인딩됩니다. +List(1, 2, 3) map (_ + 10) + +// 익명 블록과 적용하는 함수가 모두 인수를 하나만 받는 경우 +// 밑줄을 생략할 수도 있습니다. +List("Dom", "Bob", "Natalia") foreach println + + +// 조합기 +// 위에서 `s` 사용: +// val s = Set(1, 3, 7) + +s.map(sq) + +val sSquared = s.map(sq) + +sSquared.filter(_ < 10) + +sSquared.reduce (_+_) + +// filter 함수는 술어(A -> Boolean 함수)를 받아 +// 술어를 만족하는 모든 요소를 선택합니다 +List(1, 2, 3) filter (_ > 2) // List(3) +case class Person(name: String, age: Int) +List( + Person(name = "Dom", age = 23), + Person(name = "Bob", age = 30) +).filter(_.age > 25) // List(Person("Bob", 30)) + + +// Scala의 특정 컬렉션(예: List)에는 `foreach` 메서드가 있으며, +// Unit을 반환하는 타입을 인수로 받습니다. 즉, void 메서드입니다. +val aListOfNumbers = List(1, 2, 3, 4, 10, 20, 100) +aListOfNumbers foreach (x => println(x)) +aListOfNumbers foreach println + +// For 내포 + +for { n <- s } yield sq(n) + +val nSquared2 = for { n <- s } yield sq(n) + +for { n <- nSquared2 if n < 10 } yield n + +for { n <- s; nSquared = n * n if nSquared < 10} yield nSquared + +/* 참고: 이것들은 for 루프가 아니었습니다. for 루프의 의미는 '반복'인 반면, + for-내포는 두 데이터 집합 간의 관계를 정의합니다. */ + + +///////////////////////////////////////////////// +// 8. 암시 +///////////////////////////////////////////////// + +/* 경고 경고: 암시는 Scala의 강력한 기능 집합이므로 + * 남용하기 쉽습니다. Scala 초보자는 어떻게 작동하는지뿐만 아니라 + * 그 주변의 모범 사례를 이해할 때까지 사용하려는 유혹을 참아야 합니다. + * 이 튜토리얼에 이 섹션을 포함하는 이유는 Scala 라이브러리에서 너무 흔해서 + * 암시가 있는 라이브러리를 사용하지 않고는 의미 있는 작업을 수행하는 것이 + * 불가능하기 때문입니다. 이것은 여러분이 암시를 이해하고 작업하기 위한 것이며, + * 직접 선언하기 위한 것이 아닙니다. + */ + +// 모든 값(val, 함수, 객체 등)은 "implicit" 키워드를 사용하여 +// 암시적으로 선언될 수 있습니다. 이 예제에서는 5절의 Dog 클래스를 사용합니다. +implicit val myImplicitInt = 100 +implicit def myImplicitFunction(breed: String) = new Dog("Golden " + breed) + +// 그 자체로 implicit 키워드는 값의 동작을 변경하지 않으므로 +// 위 값은 평소와 같이 사용할 수 있습니다. +myImplicitInt + 2 // => 102 +myImplicitFunction("Pitbull").breed // => "Golden Pitbull" + +// 차이점은 이러한 값이 이제 다른 코드 조각이 +// "암시적 값"을 "필요"할 때 사용될 수 있다는 것입니다. 한 가지 그러한 상황은 +// 암시적 함수 인수입니다: +def sendGreetings(toWhom: String)(implicit howMany: Int) = + s"Hello $toWhom, $howMany blessings to you and yours!" + +// "howMany"에 대한 값을 제공하면 함수는 평소와 같이 동작합니다 +sendGreetings("John")(1000) // => "Hello John, 1000 blessings to you and yours!" + +// 그러나 암시적 매개변수를 생략하면 동일한 타입의 암시적 값이 +// 사용됩니다. 이 경우 "myImplicitInt"입니다: +sendGreetings("Jane") // => "Hello Jane, 100 blessings to you and yours!" + +// 암시적 함수 매개변수를 사용하면 다른 +// 함수형 언어의 타입 클래스를 시뮬레이션할 수 있습니다. 너무 자주 사용되어 +// 자체 약어가 있습니다. 다음 두 줄은 동일한 의미입니다: +// def foo[T](implicit c: C[T]) = ... +// def foo[T : C] = ... + + +// 컴파일러가 암시를 찾는 또 다른 상황은 +// obj.method(...) +// 이지만 "obj"에 "method"라는 메서드가 없는 경우입니다. 이 경우 +// A => B 타입의 암시적 변환이 있고, 여기서 A는 obj의 타입이고 B에 +// "method"라는 메서드가 있으면 해당 변환이 적용됩니다. 따라서 +// 위의 myImplicitFunction이 범위에 있으면 다음과 같이 말할 수 있습니다: +"Retriever".breed // => "Golden Retriever" +"Sheperd".bark // => "Woof, woof!" + +// 여기서 String은 먼저 위 함수를 사용하여 Dog로 변환된 다음 +// 적절한 메서드가 호출됩니다. 이것은 매우 강력한 기능이지만, +// 다시 말하지만, 가볍게 사용해서는 안 됩니다. 사실, 위의 암시적 함수를 +// 정의했을 때 컴파일러는 정말로 무엇을 하는지 모르는 한 +// 이렇게 해서는 안 된다는 경고를 했을 것입니다. + + +///////////////////////////////////////////////// +// 9. 기타 +///////////////////////////////////////////////// + +// 항목 가져오기 +import scala.collection.immutable.List + +// 모든 "하위 패키지" 가져오기 +import scala.collection.immutable._ + +// 한 문장으로 여러 클래스 가져오기 +import scala.collection.immutable.{List, Map} + +// '=>'를 사용하여 가져오기 이름 바꾸기 +import scala.collection.immutable.{List => ImmutableList} + +// 일부를 제외하고 모든 클래스 가져오기. 다음은 Map과 Set을 제외합니다: +import scala.collection.immutable.{Map => _, Set => _, _} + +// Java 클래스도 가져올 수 있습니다. Scala 구문을 사용할 수 있습니다 +import java.swing.{JFrame, JWindow} + +// 프로그램의 진입점은 Scala 파일에서 단일 메서드 main이 있는 객체를 사용하여 +// 정의됩니다: +object Application { + def main(args: Array[String]): Unit = { + // 여기에 내용이 들어갑니다. + } +} + +// 파일에는 여러 클래스와 객체가 포함될 수 있습니다. scalac으로 컴파일 + + +// 입출력 + +// 파일을 한 줄씩 읽으려면 +import scala.io.Source +for(line <- Source.fromFile("myfile.txt").getLines()) + println(line) + +// 파일을 쓰려면 Java의 PrintWriter를 사용하십시오 +val writer = new PrintWriter("myfile.txt") +writer.write("Writing line for line" + util.Properties.lineSeparator) +writer.write("Another line here" + util.Properties.lineSeparator) +writer.close() +``` + +## 추가 자료 + +* [성급한 사람들을 위한 스칼라](http://horstmann.com/scala/) +* [트위터 스칼라 스쿨](http://twitter.github.io/scala_school/) +* [스칼라 문서](http://docs.scala-lang.org/) +* [브라우저에서 스칼라 사용해보기](http://scalatutorials.com/tour/) +* [스칼라 사용자 그룹 가입](https://groups.google.com/forum/#!forum/scala-user) diff --git a/ko/sed.md b/ko/sed.md new file mode 100644 index 0000000000..f6d97ab9b9 --- /dev/null +++ b/ko/sed.md @@ -0,0 +1,245 @@ +--- +category: tool +name: sed +filename: learnsed.sed +contributors: + - ["Diomidis Spinellis", "https://www.spinellis.gr"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +__Sed__는 모든 POSIX 호환 유닉스 시스템의 표준 도구입니다. Vim, Visual Studio Code, Atom 또는 Sublime과 같은 편집기와 같습니다. 그러나 대화식으로 명령을 입력하는 대신 명령줄이나 파일로 제공합니다. + +대화형 편집기에 대한 _Sed_의 장점은 텍스트 처리 작업을 자동화하는 데 쉽게 사용할 수 있고 거대한(테라바이트 크기) 파일을 효율적으로 처리할 수 있다는 것입니다. +_grep_보다 더 복잡한 작업을 수행할 수 있으며 많은 텍스트 처리 작업에서 해당 명령은 _awk_, _Perl_ 또는 _Python_으로 작성하는 것보다 훨씬 짧습니다. + +_Sed_는 텍스트 한 줄을 읽고(기본적으로 인수로 일부 파일이 지정되지 않은 경우 표준 입력에서) 지정된 명령으로 처리한 다음 결과를 표준 출력으로 출력하여 작동합니다. +-n 명령줄 인수를 지정하여 기본 출력을 표시하지 않을 수 있습니다. + +```sed +#!/usr/bin/sed -f +# 위 줄로 시작하고 실행 권한이 부여된 파일은 일반 스크립트로 실행할 수 있습니다. + +# 주석은 이와 같습니다. + +# 명령은 단일 문자로 구성되며 대부분의 경우 적용할 줄을 지정하여 앞에 올 수 있습니다. + +# 입력의 세 번째 줄을 삭제합니다. +3d + +# sed에 대한 인수로 명령줄에 지정된 동일한 명령: +# sed 3d + +# 많은 명령의 경우 사양은 포함 범위를 선택하는 두 개의 주소로 구성될 수 있습니다. +# 주소는 숫자로 지정하거나(/로 구분된 정규식을 통해) 지정할 수 있습니다($는 마지막 줄). + +# 1-10행 삭제 +1,10d + +# 줄은 /로 구분된 정규식으로도 지정할 수 있습니다. + +# 빈 줄 삭제. +/^$/d + +# SPOILER-BEGIN으로 시작하고 SPOILER-END로 끝나는 블록을 삭제합니다. +/SPOILER-BEGIN/,/SPOILER-END/d + +# 주소가 없는 명령은 모든 줄에 적용됩니다. + +# 시각적으로 명확한 형식으로 줄을 나열합니다(예: 탭은 \t로 표시됨). +l + +# !가 접두사로 붙은 명령은 일치하지 않는 줄에 적용됩니다. +# #으로 시작하는 줄만 유지합니다. +/^#/!d + +# 다음은 가장 자주 사용되는 명령의 예입니다. + +# 한 줄에서 John의 첫 번째 항목을 Mary로 바꿉니다. +s/John/Mary/ + +# 모든 밑줄 문자를 제거합니다(전역 대체). +s/_//g + +# 모든 HTML 태그를 제거합니다. +s/<[^>]*>//g + +# 대체 문자열에서 &는 일치하는 정규식입니다. + +# 각 줄을 큰따옴표 안에 넣습니다. +s/.*/"&"/ + +# 일치하는 정규식에서 \(패턴\)은 패턴을 버퍼에 저장하는 데 사용됩니다. +# 대체 문자열에서 \1은 첫 번째 패턴을, \2는 두 번째 패턴을 나타냅니다. \u는 다음 문자를 대문자로, \l은 소문자로 변환합니다. + +# snake_case_identifiers를 camelCaseIdentifiers로 변환합니다. +s/_\(.\)/\u\1/g + + +# p(인쇄) 명령은 일반적으로 기본적으로 인쇄 기능을 비활성화하는 -n 명령줄 옵션과 함께 사용됩니다. +# ```와 ``` 사이의 모든 줄을 출력합니다. +/```/,/```/p + + +# y 명령은 한 세트의 문자를 다른 세트에 매핑합니다. +# 소수점 및 천 단위 구분 기호를 바꿉니다(1,234,343.55는 1.234.343,55가 됨). +y/.,/,./ + +# END로 시작하는 줄을 인쇄한 후 종료합니다. +/^END/q + +# 여기서 읽기를 중단해도 sed의 이점 중 80%를 얻을 수 있습니다. +# 다음은 여러 sed 명령을 지정하는 방법에 대한 예입니다. + +# 줄 바꿈이나 세미콜론으로 구분하여 여러 명령을 적용할 수 있습니다. + +# 첫 번째 줄과 마지막 줄을 삭제합니다. +1d +$d + +# 첫 번째 줄과 마지막 줄을 삭제합니다. +1d;$d + + +# { } 블록으로 명령을 그룹화할 수 있습니다. + +# 첫 번째 줄을 대문자로 변환하고 인쇄합니다. +1 { + s/./\u&/g + p +} + +# 첫 번째 줄을 대문자로 변환하고 인쇄합니다(가독성이 낮은 한 줄짜리). +1{s/./\u&/g;p;} + + +# sed 스크립트 파일을 만드는 데 관심이 없다면 여기서 읽기를 중단해도 됩니다. + +# 다음은 더 고급 명령입니다. 일반적으로 명령줄에서 지정하는 대신 파일에 넣습니다. 스크립트에서 이러한 명령을 많이 사용해야 하는 경우 Python 또는 Perl과 같은 범용 스크립팅 언어를 사용하는 것을 고려하십시오. + +# ";"로 끝나는 각 줄 뒤에 "profile();"이 포함된 줄을 추가합니다. +/;$/a\ +profile(); + +# ";"로 끝나는 각 줄 앞에 "profile();"이 포함된 줄을 삽입합니다. +/;$/i\ +profile(); + +# REDACTED 블록 안의 각 줄 텍스트를 [REDACTED]로 변경합니다. +/REDACTED-BEGIN/,/REDACTED-END/c\ +[REDACTED] + +# style.css 파일을 읽고 출력하여 "" 태그를 바꿉니다. +// { + r style.css + d +} + +# REDACTED 블록 안의 각 줄을 [REDACTED]로 변경합니다. +# 또한 수정된 텍스트의 복사본을 redacted.txt 파일에 씁니다(추가). +/REDACTED-BEGIN/,/REDACTED-END/ { + w redacted.txt + c\ + [REDACTED] +} + +# 지금까지 설명한 모든 작업은 "패턴 공간"이라는 버퍼에서 작동합니다. +# 또한 sed는 "홀드 공간"이라는 또 다른 버퍼를 제공합니다. +# 다음 명령은 두 버퍼에서 작동하며 상태를 유지하거나 여러 줄을 결합하는 데 사용할 수 있습니다. + +# 패턴 공간의 내용을 홀드 공간의 내용으로 바꿉니다. +g + +# 줄 바꿈 문자와 홀드 공간의 내용을 패턴 공간에 추가합니다. +G + +# 홀드 공간의 내용을 패턴 공간의 내용으로 바꿉니다. +h + +# 줄 바꿈 문자와 패턴 공간의 내용을 홀드 공간에 추가합니다. +H + +# 첫 번째 줄 바꿈 문자까지 패턴 공간의 초기 세그먼트를 삭제하고 다음 주기를 시작합니다. +D + +# 패턴 공간의 내용을 홀드 공간의 내용으로 바꿉니다. +g + +# 줄 바꿈 문자와 홀드 공간의 내용을 패턴 공간에 추가합니다. +G + +# 홀드 공간의 내용을 패턴 공간의 내용으로 바꿉니다. +h + +# 줄 바꿈 문자와 패턴 공간의 내용을 홀드 공간에 추가합니다. +H + +# 기본 출력이 표시되지 않은 경우 패턴 공간을 표준 출력에 쓰고 패턴 공간을 다음 입력 줄로 바꿉니다. +n + +# 추가된 자료를 원래 내용과 구분하기 위해 포함된 줄 바꿈 문자를 사용하여 다음 입력 줄을 패턴 공간에 추가합니다. 현재 줄 번호가 변경됩니다. +N + +# 첫 번째 줄 바꿈 문자까지 패턴 공간을 표준 출력에 씁니다. +P + +# 패턴과 홀드 공간의 내용을 바꿉니다. +x + +# 다음은 일부 버퍼 명령의 전체 예입니다. +# 파일의 첫 번째 줄을 끝으로 이동합니다. +1 { + h + d +} + +$ { + p + x +} + + +# 세 가지 sed 명령이 스크립트의 제어 흐름에 영향을 미칩니다. + +# 이 스크립트 위치를 "my_label"로 지정하면 "b" 및 "t" 명령이 분기할 수 있습니다. +:my_label + +# my_label 위치에서 명령 실행을 계속합니다. +b my_label + +# 스크립트 끝으로 분기합니다. +b + +# 가장 최근에 입력 줄을 읽거나 "t"(테스트) 함수를 실행한 이후에 대체가 이루어진 경우 my_label로 분기합니다. +t my_label + +# 다음은 분기의 전체 예입니다. +# 백슬래시로 끝나는 줄을 공백으로 구분된 단일 줄로 결합합니다. + +# 이 위치를 "loop"로 지정합니다. +: loop +# 백슬래시로 끝나는 줄에서 +/\\$/ { + # 다음 줄을 읽고 패턴 공간에 추가합니다. + N + # 백슬래시 줄 바꿈을 공백으로 바꿉니다. + s/\\\n/ / + # 이 줄의 끝을 테스트하기 위해 맨 위로 분기합니다. + b loop +} +``` + +더 읽을거리: + +* [The Open Group: sed - stream editor](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html) + sed에 대한 POSIX 표준입니다. + 최대 이식성을 위해 이를 따르십시오. +* [FreeBSD sed -- stream editor](https://www.freebsd.org/cgi/man.cgi?query=sed&sektion=&n=1) + BSD 매뉴얼 페이지입니다. + 이 버전의 sed는 BSD 시스템과 macOS에서 실행됩니다. +* [Project GNU: sed, a stream editor](https://www.gnu.org/software/sed/manual/sed.html) + GNU 매뉴얼 페이지입니다. GNU sed는 대부분의 Linux 시스템에서 찾을 수 있습니다. +* [Lee E. McMahon: SED -- A Non-interactive Text Editor](https://wolfram.schneider.org/bsd/7thEdManVol2/sed/sed.pdf) + 원본 sed 문서 +* [A collection of sed resources](http://sed.sourceforge.net/) +* [The sed FAQ](http://sed.sourceforge.net/sedfaq.html) diff --git a/ko/self.md b/ko/self.md new file mode 100644 index 0000000000..534575844a --- /dev/null +++ b/ko/self.md @@ -0,0 +1,153 @@ +--- +name: Self +contributors: + - ["Russell Allen", "http://github.com/russellallen"] +filename: learnself.self +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Self는 자체 JIT VM에서 실행되는 빠른 프로토타입 기반 객체 지향 언어입니다. 대부분의 개발은 통합된 브라우저와 디버거가 있는 *morphic*이라는 시각적 개발 환경을 통해 라이브 객체와 상호 작용하여 수행됩니다. + +Self의 모든 것은 객체입니다. 모든 계산은 객체에 메시지를 보내는 것으로 수행됩니다. Self의 객체는 키-값 슬롯의 집합으로 이해할 수 있습니다. + +# 객체 생성 + +내장된 Self 파서는 메서드 객체를 포함한 객체를 생성할 수 있습니다. + +``` +"이것은 주석입니다" + +"문자열:" +'이스케이프 문자가 있는 문자열입니다.\n' + +"30비트 정수" +23 + +"30비트 부동 소수점" +3.2 + +"-20" +-14r16 + +"하나의 메시지 'x'만 이해하고 20을 반환하는 객체" +(| + x = 20. +|) + +"x 슬롯을 설정하는 'x:'도 이해하는 객체" +(| + x <- 20. +|) + +"x의 값을 두 배로 늘린 다음 객체를 반환하는 'doubleX' 메서드를 이해하는 객체" +(| + x <- 20. + doubleX = (x: x * 2. self) +|) + +"'traits point'가 이해하는 모든 메시지를 이해하는 객체". 파서는 'lobby'라는 알려진 객체에 'traits' 다음 'point' 메시지를 보내 'traits point'를 찾습니다. 또한 로비에 'true' 메시지를 보내 'true' 객체를 찾습니다." +(| parent* = traits point. + x = 7. + y <- 5. + isNice = true. +|) +``` + +# 객체에 메시지 보내기 + +메시지는 단항, 이항 또는 키워드일 수 있습니다. 우선순위는 그 순서대로입니다. Smalltalk와 달리 이항 메시지의 우선순위는 지정해야 하며, 첫 번째 키워드 다음의 모든 키워드는 대문자로 시작해야 합니다. 메시지는 공백으로 대상과 구분됩니다. + +``` +"단항 메시지, '23' 객체에 'printLine'을 보내 stdout에 '23' 문자열을 출력하고 수신 객체(즉, 23)를 반환합니다" +23 printLine + +"'23'에 '7'과 함께 '+' 메시지를 보낸 다음 결과에 '8'과 함께 '*' 메시지를 보냅니다" +(23 + 7) * 8 + +"'2'에 '8'과 함께 'power:'를 보내 256을 반환합니다" +2 power: 8 + +"'hello'에 인수 'e'와 '-1'을 사용하여 'keyOf:IfAbsent:'를 보냅니다. 'hello'에서 'e'의 인덱스인 1을 반환합니다." +'hello' keyOf: 'e' IfAbsent: -1 +``` + +# 블록 + +Self는 Smalltalk 및 Ruby와 같이 블록을 통해 제어 흐름을 정의합니다. 블록은 다음과 같은 형식의 지연된 계산입니다: + +``` +[|:x. localVar| x doSomething with: localVar] +``` + +블록 사용 예: + +``` +"returns 'HELLO'" +'hello' copyMutable mapBy: [|:c| c capitalize] + +"returns 'Nah'" +'hello' size > 5 ifTrue: ['Yay'] False: ['Nah'] + +"returns 'HaLLO'" +'hello' copyMutable mapBy: [|:c| + c = 'e' ifTrue: [c capitalize] + False: ['a']] +``` + +여러 표현식은 마침표로 구분됩니다. ^는 즉시 반환됩니다. + +``` +"returns An 'E'! How icky!" +'hello' copyMutable mapBy: [|:c. tmp <- ''| + tmp: c capitalize. + tmp = 'E' ifTrue: [^ 'An \'E\'! How icky!']. + c capitalize + ] +``` + +블록은 'value' 메시지를 보내 수행되며 컨텍스트를 상속(위임)합니다: + +``` +"returns 0" +[|x| + x: 15. + "두 번째 블록에 'value'를 보낸 결과가 'true' 객체인 동안 첫 번째 블록에 'value'를 반복적으로 보냅니다" + [x > 0] whileTrue: [x: x - 1]. + x +] value +``` + +# 메서드 + +메서드는 블록과 같지만 컨텍스트 내에 있지 않고 대신 슬롯의 값으로 저장됩니다. Smalltalk와 달리 메서드는 기본적으로 'self'가 아닌 최종 값을 반환합니다. + +``` +"여기 할당 가능한 슬롯 'x'와 메서드 'reduceXTo: y'가 있는 객체가 있습니다. 이 객체에 'reduceXTo: 10' 메시지를 보내면 'x' 슬롯에 '10' 객체를 넣고 원래 객체를 반환합니다" +(| + x <- 50. + reduceXTo: y = ( + [x > y] whileTrue: [x: x - 1]. + self) +|) +. +``` + +# 프로토타입 + +Self에는 클래스가 없습니다. 객체를 얻는 방법은 프로토타입을 찾아 복사하는 것입니다. + +``` +| d | +d: dictionary copy. +d at: 'hello' Put: 23 + 8. +d at: 'goodbye' Put: 'No!. +"Prints No!" +( d at: 'goodbye' IfAbsent: 'Yes! ) printLine. +"Prints 31" +( d at: 'hello' IfAbsent: -1 ) printLine. +``` + +# 추가 정보 + +[Self 핸드북](http://handbook.selflanguage.org)에는 훨씬 더 많은 정보가 있으며, [홈페이지](http://www.selflanguage.org)에서 Self를 다운로드하여 직접 경험하는 것보다 더 좋은 것은 없습니다. diff --git a/ko/set-theory.md b/ko/set-theory.md new file mode 100644 index 0000000000..7ef0616bff --- /dev/null +++ b/ko/set-theory.md @@ -0,0 +1,136 @@ +--- +category: 알고리즘 및 자료 구조 +name: 집합론 +contributors: + - ["Andrew Ryan Davis", "https://github.com/AndrewDavis1191"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +집합론은 집합, 그 연산 및 속성을 연구하는 수학의 한 분야입니다. + +* 집합은 서로소 항목의 모음입니다. + +## 기본 기호 + +### 연산자 +* 합집합 연산자, `∪`, "컵"이라고 발음하며 "또는"을 의미합니다. +* 교집합 연산자, `∩`, "캡"이라고 발음하며 "그리고"를 의미합니다. +* 차집합 연산자, `\`, "없이"를 의미합니다. +* 여집합 연산자, `'`, "의 역"을 의미합니다. +* 곱집합 연산자, `×`, "의 데카르트 곱"을 의미합니다. + +### 한정자 +* 콜론, `:`, 또는 수직 막대 `|` 한정자는 서로 바꿔 쓸 수 있으며 "그런"을 의미합니다. +* 소속 한정자, `∈`, "에 속한다"를 의미합니다. +* 부분집합 한정자, `⊆`, "의 부분집합이다"를 의미합니다. +* 진부분집합 한정자, `⊂`, "의 부분집합이지만 같지는 않다"를 의미합니다. + +### 표준 집합 +* `∅`, 공집합, 즉 항목을 포함하지 않는 집합입니다. +* `ℕ`, 모든 자연수의 집합입니다. +* `ℤ`, 모든 정수의 집합입니다. +* `ℚ`, 모든 유리수의 집합입니다. +* `ℝ`, 모든 실수의 집합입니다. + +표준 집합에 관해 언급할 몇 가지 주의 사항이 있습니다: +1. 공집합은 항목을 포함하지 않지만, 공집합은 그 자체의 부분집합입니다 (그리고 실제로 다른 모든 집합의 부분집합입니다). +2. 수학자들은 일반적으로 0이 자연수인지에 대해 보편적으로 동의하지 않으며, 교과서는 일반적으로 저자가 0을 자연수로 간주하는지 여부를 명시적으로 기술합니다. + + +### 기수 + +집합의 기수 또는 크기는 집합에 있는 항목의 수에 따라 결정됩니다. 기수 연산자는 이중 파이프 `|...|`로 표시됩니다. + +예를 들어, `S = { 1, 2, 4 }`이면 `|S| = 3`입니다. + +### 공집합 +* 공집합은 불가능한 조건을 사용하여 집합 작성자 표기법으로 구성할 수 있습니다. 예: `∅ = { x : x ≠ x }` 또는 `∅ = { x : x ∈ N, x < 0 }`. +* 공집합은 항상 고유합니다 (즉, 공집합은 하나뿐입니다). +* 공집합은 모든 집합의 부분집합입니다. +* 공집합의 기수는 0입니다. 즉, `|∅| = 0`. + +## 집합 표현 + +### 리터럴 집합 + +집합은 집합에 포함된 객체의 전체 목록을 제공하여 문자 그대로 구성할 수 있습니다. 예를 들어, `S = { a, b, c, d }`. + +긴 목록은 컨텍스트가 명확한 한 줄임표로 줄일 수 있습니다. 예를 들어, `E = { 2, 4, 6, 8, ... }`는 명시적으로 네 개만 썼지만 무한한 수의 객체를 포함하는 모든 짝수의 집합임이 분명합니다. + +### 집합 작성자 + +집합 작성자 표기법은 집합을 구성하는 더 설명적인 방법입니다. `S = { 주체 : 술어 }`와 같이 _주체_와 _술어_에 의존합니다. 예를 들어, + +``` +A = { x : x는 모음 } = { a, e, i, o, u } +B = { x : x ∈ N, x < 10 } = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } +C = { x : x = 2k, k ∈ N } = { 0, 2, 4, 6, 8, ... } +``` + +때로는 술어가 주체로 "누출"될 수 있습니다. 예: + +``` +D = { 2x : x ∈ N } = { 0, 2, 4, 6, 8, ... } +``` + +## 관계 + +### 소속 + +* 값 `a`가 집합 `A`에 포함되어 있으면 `a`는 `A`에 속한다고 말하며 이것을 기호로 `a ∈ A`로 나타냅니다. +* 값 `a`가 집합 `A`에 포함되어 있지 않으면 `a`는 `A`에 속하지 않는다고 말하며 이것을 기호로 `a ∉ A`로 나타냅니다. + +### 동등성 + +* 두 집합이 동일한 항목을 포함하면 두 집합은 같다고 말합니다. 예: `A = B`. +* 집합 동등성을 결정할 때 순서는 중요하지 않습니다. 예: `{ 1, 2, 3, 4 } = { 2, 3, 1, 4 }`. +* 집합은 서로소이므로 요소가 반복될 수 없습니다. 예: `{ 1, 2, 2, 3, 4, 3, 4, 2 } = { 1, 2, 3, 4 }`. +* 두 집합 `A`와 `B`가 같다는 것은 `A ⊆ B`이고 `B ⊆ A`인 경우에만 해당됩니다. + +## 특수 집합 + +### 멱집합 +* `A`를 임의의 집합이라고 합시다. `A`의 모든 가능한 부분집합을 포함하는 집합을 "멱집합"이라고 하며 `P(A)`로 씁니다. 집합 `A`에 `n`개의 요소가 있으면 `P(A)`에는 `2^n`개의 요소가 있습니다. + +``` +P(A) = { x : x ⊆ A } +``` + +## 두 집합 간의 집합 연산 +### 합집합 +두 집합 `A`와 `B`가 주어졌을 때, 두 집합의 합집합은 `A` 또는 `B`에 나타나는 항목이며, `A ∪ B`로 씁니다. + +``` +A ∪ B = { x : x ∈ A ∪ x ∈ B } +``` + +### 교집합 +두 집합 `A`와 `B`가 주어졌을 때, 두 집합의 교집합은 `A`와 `B` 모두에 나타나는 항목이며, `A ∩ B`로 씁니다. + +``` +A ∩ B = { x : x ∈ A, x ∈ B } +``` + +### 차집합 +두 집합 `A`와 `B`가 주어졌을 때, `A`와 `B`의 집합 차이는 `B`에 속하지 않는 `A`의 모든 항목입니다. + +``` +A \ B = { x : x ∈ A, x ∉ B } +``` + +### 대칭차집합 +두 집합 `A`와 `B`가 주어졌을 때, 대칭차집합은 교집합에 나타나지 않는 `A`와 `B`의 모든 항목입니다. + +``` +A △ B = { x : ((x ∈ A) ∩ (x ∉ B)) ∪ ((x ∈ B) ∩ (x ∉ A)) } + +A △ B = (A \ B) ∪ (B \ A) +``` + +### 데카르트 곱 +두 집합 `A`와 `B`가 주어졌을 때, `A`와 `B` 사이의 데카르트 곱은 `A`와 `B`의 모든 항목 조합을 포함하는 집합으로 구성됩니다. + +``` +A × B = { (x, y) | x ∈ A, y ∈ B } +``` diff --git a/ko/shutit.md b/ko/shutit.md new file mode 100644 index 0000000000..c6314ba5ec --- /dev/null +++ b/ko/shutit.md @@ -0,0 +1,308 @@ +--- +category: framework +name: ShutIt +contributors: + - ["Ian Miell", "http://ian.meirionconsulting.tk"] +filename: learnshutit.py +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +ShutIt은 사용하기 쉽게 설계된 셸 자동화 프레임워크입니다. + +Python 기반 expect 클론(pexpect)을 감싸는 래퍼입니다. + +'고통 없는 expect'로 볼 수 있습니다. + +pip 설치로 사용할 수 있습니다. + +## Hello World + +가장 간단한 예부터 시작하겠습니다. example.py라는 파일을 만드십시오: + +```python +import shutit +session = shutit.create_session('bash') +session.send('echo Hello World', echo=True) +``` + +다음과 같이 실행합니다: + +```bash +python example.py +``` + +출력: + +```bash +$ python example.py +echo "Hello World" +echo "Hello World" +Hello World +Ians-MacBook-Air.local:ORIGIN_ENV:RhuebR2T# +``` + +'send'의 첫 번째 인수는 실행하려는 명령어입니다. 'echo' +인수는 터미널 상호 작용을 출력합니다. 기본적으로 ShutIt은 조용합니다. + +'send'는 expect에서 익숙할 수 있는 프롬프트와 'expect'에 대한 모든 +혼란스러운 부분을 처리합니다. + +## 서버에 로그인 + +서버에 로그인하여 명령어를 실행하고 싶다고 가정해 봅시다. example.py를 +다음과 같이 변경하십시오: + +```python +import shutit +session = shutit.create_session('bash') +session.login('ssh you@example.com', user='you', password='mypassword') +session.send('hostname', echo=True) +session.logout() +``` + +이렇게 하면 서버에 로그인하고(세부 정보를 바꾸면) +호스트 이름을 출력합니다. + +``` +$ python example.py +hostname +hostname +example.com +example.com:cgoIsdVv:heDa77HB# +``` + +물론 이것은 안전하지 않습니다! 대신 다음을 실행할 수 있습니다: + +```python +import shutit +session = shutit.create_session('bash') +password = session.get_input('', ispass=True) +session.login('ssh you@example.com', user='you', password=password) +session.send('hostname', echo=True) +session.logout() +``` + +이렇게 하면 암호를 입력해야 합니다: + +``` +$ python example.py +Input Secret: +hostname +hostname +example.com +example.com:cgoIsdVv:heDa77HB# +``` + +다시 말하지만, 'login' 메서드는 로그인에서 변경되는 프롬프트를 처리합니다. +ShutIt에 로그인 명령어, 로그인할 것으로 예상되는 사용자, 암호(필요한 경우)를 +제공하면 ShutIt이 나머지를 처리합니다. + +'logout'은 'login'의 종료를 처리하며, 프롬프트 변경 사항을 +처리합니다. + +## 여러 서버에 로그인 + +두 개의 서버로 구성된 서버 팜이 있고 둘 다에 로그인하고 싶다고 가정해 봅시다. +두 개의 세션을 만들고 유사한 로그인 및 전송 명령어를 실행하기만 하면 됩니다: + +```python +import shutit +session1 = shutit.create_session('bash') +session2 = shutit.create_session('bash') +password1 = session1.get_input('Password for server1', ispass=True) +password2 = session2.get_input('Password for server2', ispass=True) +session1.login('ssh you@one.example.com', user='you', password=password1) +session2.login('ssh you@two.example.com', user='you', password=password2) +session1.send('hostname', echo=True) +session2.send('hostname', echo=True) +session1.logout() +session2.logout() +``` + +다음과 같이 출력됩니다: + +```bash +$ python example.py +Password for server1 +Input Secret: + +Password for server2 +Input Secret: +hostname +hostname +one.example.com +one.example.com:Fnh2pyFj:qkrsmUNs# hostname +hostname +two.example.com +two.example.com:Gl2lldEo:D3FavQjA# +``` + +## 예: 여러 서버 모니터링 + +위의 내용을 명령어 출력을 검사하는 일부 논리를 추가하여 +간단한 모니터링 도구로 바꿀 수 있습니다: + +```python +import shutit +capacity_command="""df / | awk '{print $5}' | tail -1 | sed s/[^0-9]//""" +session1 = shutit.create_session('bash') +session2 = shutit.create_session('bash') +password1 = session.get_input('Password for server1', ispass=True) +password2 = session.get_input('Password for server2', ispass=True) +session1.login('ssh you@one.example.com', user='you', password=password1) +session2.login('ssh you@two.example.com', user='you', password=password2) +capacity = session1.send_and_get_output(capacity_command) +if int(capacity) < 10: + print('RUNNING OUT OF SPACE ON server1!') +capacity = session2.send_and_get_output(capacity_command) +if int(capacity) < 10: + print('RUNNING OUT OF SPACE ON server2!') +session1.logout() +session2.logout() +``` + +여기서는 'send\_and\_get\_output' 메서드를 사용하여 용량 +명령어(df)의 출력을 검색합니다. + +위의 작업을 수행하는 훨씬 더 우아한 방법이 있습니다(예: +반복할 서버의 사전을 갖는 것). 하지만 Python이 얼마나 +똑똑해야 하는지는 여러분에게 달려 있습니다. + +## 더 복잡한 IO - 예상 + +자동화하려는 대화형 명령줄 애플리케이션과의 +상호 작용이 있다고 가정해 봅시다. 여기서는 telnet을 간단한 예로 사용합니다: + +```python +import shutit +session = shutit.create_session('bash') +session.send('telnet', expect='elnet>', echo=True) +session.send('open google.com 80', expect='scape character', echo=True) +session.send('GET /', echo=True, check_exit=False) +session.logout() +``` + +'expect' 인수를 참고하십시오. 계속하려면 telnet의 프롬프트 일부만 +제공하면 됩니다. + +또한 위의 'check\_exit' 인수도 새로운 것입니다. 나중에 다시 +살펴보겠습니다. 위의 출력은 다음과 같습니다: + +```bash +$ python example.py +telnet +telnet> open google.com 80 +Trying 216.58.214.14... +Connected to google.com. +Escape character is '^]'. +GET / +HTTP/1.0 302 Found +Cache-Control: private +Content-Type: text/html; charset=UTF-8 +Referrer-Policy: no-referrer +Location: http://www.google.co.uk/?gfe_rd=cr&ei=huczWcj3GfTW8gfq0paQDA +Content-Length: 261 +Date: Sun, 04 Jun 2017 10:57:10 GMT + + +302 Moved +

302 Moved

+The document has moved +
+here +. + +Connection closed by foreign host. +``` + +이제 'check\_exit=False'로 돌아가겠습니다. telnet 명령어가 실패 종료 +코드(1)를 반환하고 스크립트가 실패하지 않도록 하려면 'check\_exit=False'를 +설정하여 ShutIt에 종료 코드에 신경 쓰지 않는다는 것을 알립니다. + +해당 인수를 전달하지 않으면 ShutIt은 통신할 터미널이 있는 경우 +대화형 터미널을 제공합니다. 이것을 '일시 중지 지점'이라고 합니다. + +## 일시 중지 지점 + +다음을 호출하여 언제든지 '일시 중지 지점'을 트리거할 수 있습니다. + +```python +[...] +session.pause_point('This is a pause point') +[...] +``` + +스크립트 내에서 CTRL과 ']'를 동시에 눌러 스크립트를 계속 진행합니다. +이것은 디버깅에 좋습니다: 일시 중지 지점을 추가하고, 둘러본 다음, +계속하십시오. 이것을 시도해 보십시오: + +```python +import shutit +session = shutit.create_session('bash') +session.pause_point('Have a look around!') +session.send('echo "Did you enjoy your pause point?"', echo=True) +``` + +다음과 같은 출력으로: + +```bash +$ python example.py +Have a look around! + +Ians-Air.home:ORIGIN_ENV:I00LA1Mq# bash +imiell@Ians-Air:/space/git/shutit ⑂ master +  +CTRL-] caught, continuing with run... +2017-06-05 15:12:33,577 INFO: Sending: exit +2017-06-05 15:12:33,633 INFO: Output (squashed): exitexitIans-Air.home:ORIGIN_ENV:I00LA1Mq# [...] +echo "Did you enjoy your pause point?" +echo "Did you enjoy your pause point?" +Did you enjoy your pause point? +Ians-Air.home:ORIGIN_ENV:I00LA1Mq# +``` + +## 더 복잡한 IO - 백그라운드 작업 + +'여러 서버 모니터링' 예제로 돌아가서, 각 서버에서 +실행하려는 장기 실행 작업이 있다고 상상해 봅시다. 기본적으로 ShutIt은 +직렬로 작동하므로 시간이 오래 걸립니다. 하지만 백그라운드에서 작업을 +실행하여 속도를 높일 수 있습니다. + +여기서는 간단한 명령어: 'sleep 60'으로 예를 들어 볼 수 있습니다. + +```python +import shutit +import time +long_command="""sleep 60""" +session1 = shutit.create_session('bash') +session2 = shutit.create_session('bash') +password1 = session1.get_input('Password for server1', ispass=True) +password2 = session2.get_input('Password for server2', ispass=True) +session1.login('ssh you@one.example.com', user='you', password=password1) +session2.login('ssh you@two.example.com', user='you', password=password2) +start = time.time() +session1.send(long_command, background=True) +session2.send(long_command, background=True) +print('That took: ' + str(time.time() - start) + ' seconds to fire') +session1.wait() +session2.wait() +print('That took: ' + str(time.time() - start) + ' seconds to complete') +``` + +제 노트북은 두 명령어를 실행하는 데 0.5초가 걸렸고, 그 다음 +1분 이상 걸려 완료되었습니다('wait' 메서드 사용). + +다시 말하지만, 이것은 사소하지만, 수백 대의 서버를 이와 같이 +관리해야 한다고 상상해 보십시오. 그러면 몇 줄의 코드와 하나의 +Python 가져오기로 얼마나 강력한 힘을 발휘할 수 있는지 알 수 있습니다. + +## 더 알아보기 + +ShutIt으로 할 수 있는 일은 훨씬 더 많습니다. + +더 알아보려면 다음을 참조하십시오: + +[ShutIt](https://ianmiell.github.io/shutit/) +[GitHub](https://github.com/ianmiell/shutit/blob/master/README.md) + +이것은 더 넓은 자동화 프레임워크이며, 위의 내용은 '독립 실행형 모드'입니다. diff --git a/ko/sing.md b/ko/sing.md new file mode 100644 index 0000000000..87cbb232b4 --- /dev/null +++ b/ko/sing.md @@ -0,0 +1,443 @@ +--- +name: Sing +filename: learnsing.sing +contributors: + - ["Maurizio De Girolami", "https://github.com/mdegirolami"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- +sing의 목적은 고성능 애플리케이션을 위한 c++의 좋은 대체재가 될 수 있는 간단하고 안전하며 빠른 언어를 제공하는 것입니다. + +Sing은 사람이 읽을 수 있는 고품질의 c++로 컴파일되기 때문에 쉬운 선택입니다. + +그 때문에 Sing으로 잠시 작업하다가 어느 시점에 Sing이 더 이상 마음에 들지 않는다는 것을 발견하더라도, 멋지고 깨끗한 c++ 코드가 남기 때문에 작업한 것을 잃지 않습니다. + +어떤 면에서는 Sing을 몇 가지 모범 사례를 강제하는 방식으로 c++를 작성하는 도구로 생각할 수도 있습니다. + +```go +/* 여러 줄 주석. + /* 중첩될 수 있습니다 */ + 코드의 일부를 주석 처리하는 데 사용하십시오. + 중간 c++ 코드에 흔적을 남기지 않습니다. + (sing은 멋진 사람이 읽을 수 있는 c++로 번역됩니다) +*/ + +// 한 줄 주석은 문이나 선언 앞에만 올 수 있습니다... +// ...또는 문이나 선언의 첫 줄 오른쪽에. +// 한 줄 주석은 c++로 유지됩니다. +// +// 여기서는 다른 파일의 공개 선언을 사용해야 하는지 선언합니다. +// (이 경우 'sio', 'sys' 파일에서) +requires "sio"; +requires "sys"; + +// +// sing 함수 선언. +// 모든 선언은 'public' 키워드로 공개할 수 있습니다. +// 모든 선언은 선언 유형을 지정하는 키워드로 시작합니다 +// (이 경우 함수는 fn) 그런 다음 이름, 인수 및 +// 반환 유형이 따릅니다. +// +// 각 인수는 방향 한정자(in, out, io)로 시작하여 +// 인수가 입력인지, 출력인지 또는 둘 다인지 알려줍니다... +// ...그런 다음 인수 이름과 유형이 따릅니다. +public fn singmain(in argv [*]string) i32 +{ + // print는 sio 파일에서 가져오고 콘솔에 문자열을 보냅니다. + sio.print("Hello World\n"); + + // 유형 변환은 (expression) 형식으로 허용됩니다. + sio.print(string(sum(5, 10)) + "\n"); + + // 명확성을 위해 인수 뒤에 ':'로 구분된 이름을 지정할 수 있습니다. + var result i32; + recursive_power(10:base, 3:exponent, result); + + // '사용되지 않음' 오류를 피하기 위해 여기에서 참조됩니다. + learnTypes(); + + // 함수는 일부 기본 유형의 단일 값만 반환할 수 있습니다. + return(0); +} + +// 쉼표로 구분하여 원하는 만큼 인수를 가질 수 있습니다. +// 'in' 방향 한정자를 생략할 수도 있습니다(기본값임). +fn sum(arg1 i32, arg2 i32) i32 +{ + // 'fn'이 함수를 선언하는 것처럼 'let'은 상수를 선언합니다. + // 상수의 경우 이니셜라이저를 배치하면 유형을 생략할 수 있습니다. + let the_sum = arg1 + arg2; + + return(the_sum); +} + +// 인수는 참조로 전달됩니다. 즉, 함수 본문에서 +// 인수 이름을 사용하여 전달된 변수를 참조합니다. +// 예: 재귀 스택의 모든 함수는 singmain 함수에서 제공하는 +// 동일한 'result' 변수에 액세스합니다. +fn recursive_power(base i32, exponent i32, out result i32) void +{ + if (exponent == 0) { + result = 1; + } else { + recursive_power(base, exponent - 1, result); + result *= base; + } +} + +//********************************************************** +// +// 유형 +// +//********************************************************** +fn learnTypes() void +{ + // var 키워드는 변경 가능한 변수를 선언합니다. + // 이 경우 UTF-8로 인코딩된 문자열 + var my_name string; + + // 8..64비트 크기의 정수 + var int0 i8; + var int1 i16; + var int2 i32; + var int3 i64; + + // 부호 없는 정수 + var uint0 u8; + var uint1 u16; + var uint2 u32; + var uint3 u64; + + // 부동 소수점 + var float0 f32; + var float1 f64; + + // 복소수 + var cmplx0 c64; + var cmplx1 c128; + + cmplx0 = 0; + cmplx1 = 0; + + // 그리고 물론... + var bool0 bool; + + // 유형 추론: 기본적으로 상수는 i32, f32, c64입니다. + let an_int32 = 15; + let a_float32 = 15.0; + let a_complex = 15.0 + 3i; + let a_string = "Hello !"; + let a_bool = false; + + // 다른 유형의 상수를 만들려면 변환과 유사한 구문을 사용하십시오: + // 참고: 이것은 변환이 아닙니다. 단지 유형 지정일 뿐입니다. + let a_float64 = f64(5.6); + + // 유형 정의에서 []는 "의 배열"로 읽습니다. + // 예에서 []i32 => i32의 배열. + var intarray []i32 = {1, 2, 3}; + + // 길이를 지정할 수 있습니다. 그렇지 않으면 길이는 이니셜라이저에 의해 주어집니다. + // 마지막 이니셜라이저는 추가 항목에 복제됩니다. + var sizedarray [10]i32 = {1, 2, 3}; + + // 동적 배열을 얻으려면 크기로 *를 지정하십시오(길이를 변경할 수 있음). + var dyna_array [*]i32; + + // 메서드와 유사한 함수를 호출하여 벡터에 항목을 추가할 수 있습니다. + dyna_array.push_back(an_int32); + + // 배열의 크기를 가져옵니다. sys.validate()는 c의 assert와 같습니다. + sys.validate(dyna_array.size() == 1); + + // 숫자를 문자열에 연결하는 맵. + // "map(x)..."는 "키 유형이 x이고 값이 유형인 맵..."으로 읽습니다. + var a_map map(string)i32; + + a_map.insert("one", 1); + a_map.insert("two", 2); + a_map.insert("three", 3); + let key = "two"; + + // 참고: get_safe의 두 번째 인수는 + // 키를 찾을 수 없을 때 반환될 값입니다. + sio.print("\nAnd the value is...: " + string(a_map.get_safe(key, -1))); + + // 문자열 연결 + my_name = "a" + "b"; +} + +// 열거형 유형은 불연속 집합의 값만 가질 수 있습니다. +// int와 상호 변환할 수 없습니다! +enum Stages {first, second, last} + +// 열거형 값을 참조할 수 있습니다(할당/비교하기 위해). +// 유형 이름과 태그 이름을 '.' 연산자로 구분하여 지정합니다. +var current_stage = Stages.first; + + +//********************************************************** +// +// 포인터 +// +//********************************************************** + +// 이것은 동적 벡터의 팩토리입니다. +// 유형 선언에서 '*'는 '에 대한 포인터..'로 읽습니다. +// 따라서 반환 유형은 'i32 벡터에 대한 포인터'입니다. +fn vectorFactory(first i32, last i32) *[*]i32 +{ + var buffer [*]i32; + + // 채우기 + for (value in first : last) { + buffer.push_back(value); + } + + // & 연산자는 버퍼의 주소를 반환합니다. + // 지역 변수에만 &를 사용할 수 있습니다. + // 변수에 &를 사용하면 해당 변수는 HEAP에 할당됩니다. + return(&buffer); +} + +fn usePointers() void +{ + var bufferptr = vectorFactory(0, 100); + + // 포인터를 사용하기 위해 팩토리 패턴을 사용할 필요는 없습니다. + var another_buffer [*]i32; + var another_bufferptr = &another_buffer; + + // * 연산자로 포인터를 역참조할 수 있습니다. + // sys.validate는 어설션입니다(인수가 false이면 신호 발생). + sys.validate((*bufferptr)[0] == 0); + + /* + // 변수에 대한 모든 포인터가 범위를 벗어나면 변수는 + // 더 이상 액세스할 수 없으며 삭제(해제)됩니다. + */ +} + +//********************************************************** +// +// 클래스 +// +//********************************************************** + +// 이것은 클래스입니다. 멤버 변수는 여기에서 직접 초기화할 수 있습니다. +class AClass { +public: + var public_var = 100; // 다른 변수 선언과 동일 + fn is_ready() bool; // 다른 함수 선언과 동일 + fn mut finalize() void; // 소멸자(객체 삭제 시 호출) +private: + var private_var string; + + // 멤버 변수를 변경하고 'mut'(변경 가능)로 표시해야 합니다. + fn mut private_fun(errmsg string) void; +} + +// 멤버 함수를 선언하는 방법 +fn AClass.is_ready() bool +{ + // 멤버 함수 내에서 멤버는 + // 'this' 키워드와 필드 선택기 '.'를 통해 액세스할 수 있습니다. + return(this.public_var > 10); +} + +fn AClass.private_fun(errmsg string) void +{ + this.private_var = errmsg; +} + +// 클래스 사용 +fn useAClass() void +{ + // 이 방법으로 AClass 유형의 변수를 만듭니다. + var instance AClass; + + // 그런 다음 '.' 연산자를 통해 해당 멤버에 액세스할 수 있습니다. + if (instance.is_ready()) { + instance.public_var = 0; + } +} + +//********************************************************** +// +// 인터페이스 +// +//********************************************************** + +// sing에서 인터페이스를 정의하여 다형성을 사용할 수 있습니다... +interface ExampleInterface { + fn mut eraseAll() void; + fn identify_myself() void; +} + +// 그런 다음 인터페이스를 구현하는 클래스를 만듭니다. +// 참고: 인터페이스 함수를 다시 선언할 필요가 없습니다(그리고 할 수 없습니다). +class Implementer1 : ExampleInterface { +private: + var to_be_erased i32 = 3; +public: + var only_on_impl1 = 0; +} + +class Implementer2 : ExampleInterface { +private: + var to_be_erased f32 = 3; +} + +fn Implementer1.eraseAll() void +{ + this.to_be_erased = 0; +} + +fn Implementer1.identify_myself() void +{ + sio.print("\nI'm the terrible int eraser !!\n"); +} + +fn Implementer2.eraseAll() void +{ + this.to_be_erased = 0; +} + +fn Implementer2.identify_myself() void +{ + sio.print("\nI'm the terrible float eraser !!\n"); +} + +fn interface_casting() i32 +{ + // 업캐스팅은 자동입니다(예: *Implementer1에서 *ExampleInterface로). + var concrete Implementer1; + var if_ptr *ExampleInterface = &concrete; + + // (추측하셨겠지만) '.'으로 인터페이스 멤버에 액세스할 수 있습니다. + if_ptr.identify_myself(); + + // 다운캐스팅에는 특수 구문이 필요합니다. + // (아래 조건 구조도 참조하십시오). + typeswitch(ref = if_ptr) { + case *Implementer1: return(ref.only_on_impl1); + case *Implementer2: {} + default: return(0); + } + + return(1); +} + +// 모든 루프 유형 +fn loops() void +{ + // while: 조건은 엄격하게 부울 유형이어야 합니다. + var idx = 0; + while (idx < 10) { + ++idx; + } + + // 정수 범위의 for. 마지막 값은 제외됩니다. + // 'it'은 루프에 로컬이며 이전에 선언되어서는 안 됩니다. + for (it in 0 : 10) { + } + + // 역방향 + for (it in 10 : 0) { + } + + // 구성 가능한 단계. 최종 값보다 크거나 같으면 루프가 중지됩니다. + for (it in 0 : 100 step 3) { + } + + // 보조 카운터 사용. + // 카운터는 항상 0에서 시작하여 각 반복에서 1씩 증가합니다. + for (counter, it in 3450 : 100 step -22) { + } + + // value는 차례로 배열의 모든 값을 가정합니다. + var array [*]i32 = {0, 10, 100, 1000}; + for (value in array) { + } + + // 보조 카운터와 함께 이전과 동일 + for (counter, value in array) { + } +} + +// 모든 조건 구조 +interface intface {} +class c0_test : intface {public: fn c0stuff() void;} +class delegating : intface {} + +fn conditionals(in object intface, in objptr *intface) void +{ + let condition1 = true; + let condition2 = true; + let condition3 = true; + var value = 30; + + // condition1은 부울이어야 합니다. + if (condition1) { + ++value; // 조건부 문 + } + + // else if로 조건을 연결할 수 있습니다. + if (condition1) { + ++value; + } else if (condition2) { + --value; + } + + // 다른 조건이 false이면 최종 else가 실행됩니다. + if (condition1) { + ++value; + } else if (condition2) { + --value; + } else { + value = 0; + } + + // switch 값을 기준으로 case 문을 선택합니다. + switch (value) { + case 0: sio.print("value is zero"); // 단일 문! + case 1: {} // 아무것도 안 함 + case 2: // 통과 + case 3: sio.print("value is more than one"); + case 4: { // 블록은 단일 문입니다! + value = 0; + sio.print("how big !!"); + } + default: return; // 다른 것이 일치하지 않으면 + } + + // switch와 유사하지만 인수 유형을 기준으로 case를 선택합니다. + // - object는 인터페이스 유형의 함수 인수여야 합니다. + // - case 유형은 object 인터페이스를 구현하는 클래스여야 합니다. + // - 각 case 문에서 ref는 해당 case의 클래스 유형을 가정합니다. + typeswitch(ref = object) { + case c0_test: ref.c0stuff(); + case delegating: {} + default: return; + } + + // - object는 인터페이스 포인터여야 합니다. + // - case 유형은 objptr 인터페이스를 구현하는 클래스에 대한 포인터여야 합니다. + // - 각 case 문에서 ref는 해당 case의 클래스 포인터 유형을 가정합니다. + typeswitch(ref = objptr) { + case *c0_test: { + ref.c0stuff(); + return; + } + case *delegating: {} + default: sio.print("unknown pointer type !!"); + } +} +``` + +## 더 읽을거리 + +[공식 Sing 웹사이트](https://mdegirolami.wixsite.com/singlang). + +sing을 가지고 놀고 싶다면 vscode 플러그인을 다운로드하는 것이 좋습니다. +[시작하기](https://mdegirolami.wixsite.com/singlang/copy-of-interfacing-sing-and-c-2)의 지침을 따르십시오. diff --git a/ko/smallbasic.md b/ko/smallbasic.md new file mode 100644 index 0000000000..3721517e29 --- /dev/null +++ b/ko/smallbasic.md @@ -0,0 +1,131 @@ +--- +name: SmallBASIC +filename: learnsmallbasic.bas +contributors: + - ["Chris Warren-Smith", "http://smallbasic.sourceforge.net"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +## 정보 + +SmallBASIC은 일상적인 계산, 스크립트 및 프로토타입에 이상적인 빠르고 배우기 쉬운 BASIC 언어 인터프리터입니다. SmallBASIC에는 삼각 함수, 행렬 및 대수 함수, 내장 IDE, 강력한 문자열 라이브러리, 시스템, 사운드 및 그래픽 명령과 구조화된 프로그래밍 구문이 포함되어 있습니다. + +## 개발 + +SmallBASIC은 1999년 말 니콜라스 크리스토풀로스가 팜 파일럿용으로 처음 개발했습니다. 프로젝트 개발은 2005년경부터 크리스 워렌-스미스가 계속해 왔습니다. + +SmallBASIC 버전은 프랭클린 e북맨 및 노키아 770을 포함한 여러 초기 휴대용 장치용으로 만들어졌습니다. 또한 다양한 GUI 도구 키트를 기반으로 한 다양한 데스크톱 버전이 출시되었으며, 그중 일부는 더 이상 사용되지 않습니다. 현재 지원되는 플랫폼은 SDL2 기반의 Linux 및 Windows와 NDK 기반의 Android입니다. 데스크톱 명령줄 버전도 사용할 수 있지만 일반적으로 바이너리 형식으로 릴리스되지는 않습니다. + +2008년경에 한 대기업이 비슷한 이름의 BASIC과 유사한 프로그래밍 환경을 출시했습니다. SmallBASIC은 이 다른 프로젝트와 관련이 없습니다. + +``` +REM 이것은 주석입니다 +' 그리고 이것도 주석입니다 + +REM 텍스트 인쇄 +print "hello" +? "?는 PRINT의 약자입니다" + +REM 제어 구조 +FOR index = 0 TO 10 STEP 2 + ? "이것은 줄 번호 "; index +NEXT +J=0 +REPEAT + J++ +UNTIL J=10 +WHILE J>0 + J-- +WEND + +REM Select case 문 +Select Case "Cool" + Case "null", 1,2,3,4,5,6,7,8,"Cool","blah" + Case "Not cool" + PRINT "Epic fail" + Case Else + PRINT "Fail" +End Select + +REM TRY/CATCH로 오류 잡기 +Try + fn = Freefile + Open filename For Input As #fn +Catch err + Print "failed to open" +End Try + +REM 사용자 정의 서브 및 함수 +func add2(x,y) + ' 변수는 SUB 또는 FUNC 범위 내에서 로컬로 선언될 수 있습니다. + local K + k = "이 FUNC가 반환되면 k는 더 이상 존재하지 않습니다" + add2=x+y +end +Print add2(5,5) +sub print_it(it) + print it +end +print_it "IT...." + +REM 선과 픽셀 표시 +At 0,ymax/2+txth("Q") +Color 1: ? "sin(x)": +Color 8: ? "cos(x)": +Color 12: ? "tan(x)" +Line 0,ymax/2,xmax,ymax/2 +For i=0 to xmax + Pset i,ymax/2-sin(i*2*pi/ymax)*ymax/4 color 1 + Pset i,ymax/2-cos(i*2*pi/ymax)*ymax/4 color 8 + Pset i,ymax/2-tan(i*2*pi/ymax)*ymax/4 color 12 +Next +showpage + +REM SmallBASIC은 프랙탈 및 기타 흥미로운 효과를 실험하는 데 좋습니다. +Delay 3000 +Randomize +ff = 440.03 +For j = 0 to 20 + r = rnd * 1000 % 255 + b = rnd * 1000 % 255 + g = rnd * 1000 % 255 + c = rgb(r,b,g) + ff += 9.444 + for i=0 to 25000 + f += ff + x = min(xmax, -x + cos(f*i)) + y = min(ymax, -y + sin(f*i)) + pset x, y color c + if (i%1000==0) then + showpage + fi + next +Next j + +REM 컴퓨터 역사가들을 위해 SmallBASIC은 +REM 초기 컴퓨터 서적 및 잡지에서 찾은 프로그램을 실행할 수 있습니다. 예를 들어: +10 LET A=9 +20 LET B=7 +30 PRINT A*B +40 PRINT A/B + +REM SmallBASIC은 JSON과 같은 몇 가지 최신 개념도 지원합니다. +aa = array("{\"cat\":{\"name\":\"harry\"},\"pet\":\"true\"}") +If (ismap(aa) == false) Then + throw "not an map" +End If +Print aa + +PAUSE +``` + +## 기사 + +* [시작하기](http://smallbasic.sourceforge.net/?q=node/1573) +* [SmallBASIC에 오신 것을 환영합니다](http://smallbasic.sourceforge.net/?q=node/838) + +## GitHub + +* [소스 코드](https://github.com/smallbasic/SmallBASIC) +* [참조 스냅샷](http://smallbasic.github.io/) diff --git a/ko/smalltalk.md b/ko/smalltalk.md new file mode 100644 index 0000000000..3ac71c3ff3 --- /dev/null +++ b/ko/smalltalk.md @@ -0,0 +1,1043 @@ +--- +name: Smalltalk +filename: smalltalk.st +contributors: + - ["Jigyasa Grover", "https://jigyasa-grover.github.io"] + - ["tim Rowledge", "tim@rowledge.org"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +- Smalltalk는 '비객체' 타입이 없는 완전한 객체 지향, 동적 타입, 반사적 프로그래밍 언어입니다. +- Smalltalk는 "인간-컴퓨터 공생"으로 대표되는 "새로운 컴퓨팅 세계"를 뒷받침하는 언어로 만들어졌습니다. +- 1970년대 Xerox PARC의 학습 연구 그룹(LRG)에서 Alan Kay, Dan Ingalls, Adele Goldberg, Ted Kaehler, Scott Wallace 등이 교육용, 특히 구성주의 학습을 위해 부분적으로 설계하고 만들었습니다. + +## 기본 + +### 모든 것은 객체입니다 +네, 모든 것입니다. 정수는 숫자 클래스 중 하나의 인스턴스입니다. 클래스는 Metaclass 클래스의 인스턴스이며 다른 객체와 마찬가지로 조작할 수 있습니다. 모든 클래스는 단일 클래스 트리의 일부이며, 분리된 클래스 트리는 없습니다. 스택 프레임은 객체이며 조작할 수 있으며, 이것이 디버거가 작동하는 방식입니다. 역참조하여 조작할 수 있는 메모리 위치에 대한 포인터는 없습니다. + +### 함수는 호출되지 않고, 메시지가 객체로 전송됩니다 +- 작업은 객체에 메시지를 보내는 것으로 수행되며, 객체는 해당 메시지에 응답하는 방법을 결정하고 그 결과로 메서드를 실행하며, 결국 원래 메시지를 보낸 코드로 일부 객체를 반환합니다. +- 시스템은 메시지를 수신하는 객체의 클래스를 알고 해당 클래스의 메서드 목록에서 메시지를 찾습니다. 찾지 못하면 상위 클래스에서 조회를 계속하며, 찾거나 클래스의 루트에 도달하고 여전히 관련 메서드가 없을 때까지 계속됩니다. +- 적절한 메서드를 찾으면 코드가 실행되고, 해당 메서드가 보낸 모든 메서드와 함께 동일한 프로세스가 계속됩니다. +- 적절한 메서드를 찾지 못하면 예외가 발생하며, 일반적으로 사용자에게 메시지를 이해할 수 없다는 것을 알리는 사용자 인터페이스 알림이 표시됩니다. 예외를 잡아 문제를 해결하기 위해 '무시'에서 '이 클래스에 대한 새 패키지를 로드하고 다시 시도'에 이르기까지 무언가를 수행하는 것이 전적으로 가능합니다. +- 메서드(더 엄격하게는 CompiledMethod 클래스의 인스턴스)는 바이트코드로 컴파일된 Smalltalk 코드 덩어리입니다. 실행 메서드는 처음부터 시작하여 반환이 발생하면(다음에 오는 객체를 반환하기 위해 ^를 사용함) 또는 코드 끝에 도달하면 보낸 사람에게 반환되며, 이 경우 코드를 실행하는 현재 객체가 반환됩니다. + +### 간단한 구문 +Smalltalk는 규칙이 거의 없는 간단한 구문을 가지고 있습니다. +가장 기본적인 작업은 객체에 메시지를 보내는 것입니다. +`anObject aMessage` + +세 가지 종류의 메시지가 있습니다. + +- 단항 - 인수가 없는 카멜케이스 형식으로 여러 단어가 결합된 단일 기호입니다. 예를 들어 'size', 'reverseBytes', 'convertToLargerFormatPixels' +- 이항 - 대부분의 언어에서 산술 연산에 자주 사용되는 작은 기호 집합으로, 단일 인수가 필요합니다. 예를 들어 '+', '//', '@'. 전통적인 산술 우선순위를 사용하지 않으므로 주의해야 합니다. +- 키워드 - 여러 인수를 전달할 수 있는 일반적인 형식입니다. 단항 형식과 마찬가지로 카멜케이스를 사용하여 단어를 결합하지만 인수는 콜론을 사용하여 어휘적으로 구분하여 메시지 중간에 삽입됩니다. 예를 들어 'setTemperature:', 'at:put:', 'drawFrom:to:lineWidth:fillColor:' + +#### 예시 +`result := myObject doSomethingWith: thatObject` +myObject에 'doSomethingWith:' 메시지를 보냅니다. 이것은 단일 인수가 있는 메시지이지만 아직 중요하지 않습니다. +'myObject'는 'MyExampleClass' 인스턴스이므로 시스템은 MyExampleClass가 이해하는 메시지 목록을 살펴봅니다. + +- beClever +- doWeirdThing: +- doSomethingWith + +검색에서 처음에는 일치하는 것처럼 보이지만 - 아니요, 마지막 콜론이 없습니다. 그래서 MyExampleClass의 상위 클래스인 BigExampleClass를 찾습니다. 여기에는 자체적으로 알려진 메시지 목록이 있습니다. + +- beClever +- doSomethingWith: +- buildCastleInAir +- annoyUserByDoing: + +정확히 일치하는 것을 찾아 코드를 실행하기 시작합니다: + +```smalltalk +doSomethingWith: argumentObject + self size > 4 ifTrue: [^argumentObject sizeRelatingTo: self]. +``` + +여기서 `^`를 제외한 모든 것은 더 많은 메시지를 보내는 것을 포함합니다. 언어 제어 구조라고 생각할 수 있는 `ifTrue:`조차도 Smalltalk 코드일 뿐입니다. + +`self`에 `size`를 보내는 것으로 시작합니다. `self`는 현재 코드를 실행하는 객체이므로 이 경우 우리가 시작한 myObject입니다. `size`는 객체의 크기에 대해 알려주는 매우 일반적인 메시지입니다. Smalltalk 도구를 사용하여 매우 간단하게 찾아볼 수 있습니다. 얻은 결과는 정수 4와 함께 `>` 메시지를 받으며, 이것도 객체입니다. 여기에는 기본 타입이 없습니다. `>`는 true 또는 false를 응답하는 비교입니다. 해당 부울(실제로 Smalltalk의 Boolean 객체임)은 `[]` 사이의 코드 블록을 인수로 사용하여 `ifTrue:` 메시지를 받습니다. true 부울은 해당 코드 블록을 실행하고 false는 무시할 것으로 예상됩니다. + +블록이 실행되면 인수 객체에 더 많은 메시지를 보내고 `^`를 기록하여 답을 시작 지점으로 반환하고 `result`에 할당됩니다. 블록이 무시되면 코드가 다 떨어져서 `self`가 반환되고 `result`에 할당됩니다. + +## Smalltalk 빠른 참조 치트 시트 +[Smalltalk 치트 시트](http://www.angelfire.com/tx4/cus/notes/smalltalk.html)에서 가져옴 + +#### 허용되는 문자: +- a-z +- A-Z +- 0-9 +- .+/\*~<>@%|&? +- 공백, 탭, cr, ff, lf + +#### 변수: +- 변수 이름은 사용하기 전에 선언해야 하지만 타입이 지정되지 않았습니다. +- 공유 변수(전역, 클래스 변수)는 관례적으로 대문자로 시작합니다(아래에 표시된 예약어 제외). +- 지역 변수(인스턴스 변수, 임시 변수, 메서드 및 블록 인수)는 관례적으로 소문자로 시작합니다. +- 예약어: `nil`, `true`, `false`, `self`, `super`, `thisContext` + +#### 변수 범위: +- 전역: 'Smalltalk'라는 Dictionary에 정의되어 있으며 시스템의 모든 객체에서 액세스할 수 있습니다. +- 특수: (예약됨) `Smalltalk`, `super`, `self`, `true`, `false`, `nil` +- 메서드 임시: 메서드에 로컬 +- 블록 임시: 블록에 로컬 +- 풀: Dictionary 객체의 변수로, 상속으로 직접 관련되지 않은 클래스와 공유될 수 있습니다. +- 메서드 매개변수: 들어오는 매개변수의 이름을 지정하는 자동 메서드 임시 변수입니다. 할당할 수 없습니다. +- 블록 매개변수: 들어오는 매개변수의 이름을 지정하는 자동 블록 임시 변수입니다. 할당할 수 없습니다. +- 클래스: 클래스 및 하위 클래스의 모든 인스턴스와 공유됩니다. +- 클래스 인스턴스: 클래스의 각 인스턴스에 고유합니다. 클래스 변수와 너무 흔하게 혼동됩니다. +- 인스턴스 변수: 클래스의 각 인스턴스에 고유합니다. + +`"주석은 따옴표로 묶이며 임의의 길이일 수 있습니다"` + +`"마침표(.)는 문장 구분 기호입니다. 메서드의 마지막 줄에는 필요하지 않습니다"` + +#### 트랜스크립트: +```smalltalk +Transcript clear. "트랜스크립트 창 지우기" +Transcript show: 'Hello World'. "트랜스크립트 창에 문자열 출력" +Transcript nextPutAll: 'Hello World'. "트랜스크립트 창에 문자열 출력" +Transcript nextPut: $A. "트랜스크립트 창에 문자 출력" +Transcript space. "트랜스크립트 창에 공백 문자 출력" +Transcript tab. "트랜스크립트 창에 탭 문자 출력" +Transcript cr. "캐리지 리턴 / 줄 바꿈" +'Hello' printOn: Transcript. "창에 인쇄 문자열 추가" +'Hello' storeOn: Transcript. "창에 저장 문자열 추가" +Transcript endEntry. "출력 버퍼 플러시" +``` + +#### 할당: +```smalltalk +| x y | +x _ 4. "할당 (Squeak) <-" +x := 5. "할당" +x := y := z := 6. "복합 할당" +x := (y := 6) + 1. +x := Object new. "클래스의 할당된 인스턴스에 바인딩" +``` + +#### 상수: +```smalltalk +| b | +b := true. "true 상수" +b := false. "false 상수" +x := nil. "nil 객체 상수" +x := 1. "정수 상수" +x := 3.14. "부동 소수점 상수" +x := 2e-2. "분수 상수" +x := 16r0F. "16진수 상수". +x := -1. "음수 상수" +x := 'Hello'. "문자열 상수" +x := 'I''m here'. "작은따옴표 이스케이프" +x := $A. "문자 상수" +x := $ . "문자 상수 (공백)" +x := #aSymbol. "심볼 상수" +x := #(3 2 1). "배열 상수" +x := #('abc' 2 $a). "타입 혼합 허용" +``` + +#### 불리언: +```smalltalk +| b x y | +x := 1. y := 2. +b := (x = y). "같음" +b := (x ~= y). "같지 않음" +b := (x == y). "동일함" +b := (x ~~ y). "동일하지 않음" +b := (x > y). "보다 큼" +b := (x < y). "보다 작음" +b := (x >= y). "크거나 같음" +b := (x <= y). "작거나 같음" +b := b not. "불리언 부정" +b := (x < 5) & (y > 1). "불리언 and" +b := (x < 5) | (y > 1). "불리언 or" +b := (x < 5) and: [y > 1]. "불리언 and (단락 회로)" +b := (x < 5) or: [y > 1]. "불리언 or (단락 회로)" +b := (x < 5) eqv: (y > 1). "둘 다 참이거나 둘 다 거짓인지 테스트" +b := (x < 5) xor: (y > 1). "하나가 참이고 다른 하나가 거짓인지 테스트" +b := 5 between: 3 and: 12. "사이 (포함)" +b := 123 isKindOf: Number. "객체가 클래스 또는 하위 클래스인지 테스트" +b := 123 isMemberOf: SmallInteger. "객체가 클래스 타입인지 테스트" +b := 123 respondsTo: #sqrt. "객체가 메시지에 응답하는지 테스트" +b := x isNil. "객체가 nil인지 테스트" +b := x isZero. "숫자가 0인지 테스트" +b := x positive. "숫자가 양수인지 테스트" +b := x strictlyPositive. "숫자가 0보다 큰지 테스트" +b := x negative. "숫자가 음수인지 테스트" +b := x even. "숫자가 짝수인지 테스트" +b := x odd. "숫자가 홀수인지 테스트" +b := x isLiteral. "리터럴 상수인지 테스트" +b := x isInteger. "객체가 정수인지 테스트" +b := x isFloat. "객체가 부동 소수점인지 테스트" +b := x isNumber. "객체가 숫자인지 테스트" +b := $A isUppercase. "대문자인지 테스트" +b := $A isLowercase. "소문자인지 테스트" +``` + +#### 산술 표현식: +```smalltalk +| x | +x := 6 + 3. "덧셈" +x := 6 - 3. "뺄셈" +x := 6 * 3. "곱셈" +x := 1 + 2 * 3. "평가는 항상 왼쪽에서 오른쪽으로 (1 + 2) * 3" +x := 5 / 3. "분수 결과가 있는 나눗셈" +x := 5.0 / 3.0. "부동 소수점 결과가 있는 나눗셈" +x := 5.0 // 3.0. "정수 나눗셈" +x := 5.0 \\ 3.0. "정수 나머지" +x := -5. "단항 빼기" +x := 5 sign. "숫자 부호 (1, -1 또는 0)" +x := 5 negated. "수신자 부정" +x := 1.2 integerPart. "숫자의 정수 부분 (1.0)" +x := 1.2 fractionPart. "숫자의 분수 부분 (0.2)" +x := 5 reciprocal. "역수 함수" +x := 6 * 3.1. "부동 소수점으로 자동 변환" +x := 5 squared. "제곱 함수" +x := 25 sqrt. "제곱근" +x := 5 raisedTo: 2. "거듭제곱 함수" +x := 5 raisedToInteger: 2. "정수 거듭제곱 함수" +x := 5 exp. "지수" +x := -5 abs. "절대값" +x := 3.99 rounded. "반올림" +x := 3.99 truncated. "버림" +x := 3.99 roundTo: 1. "지정된 소수 자릿수로 반올림" +x := 3.99 truncateTo: 1. "지정된 소수 자릿수로 버림" +x := 3.99 floor. "버림" +x := 3.99 ceiling. "올림" +x := 5 factorial. "팩토리얼" +x := -5 quo: 3. "0을 향해 반올림하는 정수 나눗셈" +x := -5 rem: 3. "0을 향해 반올림하는 정수 나머지" +x := 28 gcd: 12. "최대공약수" +x := 28 lcm: 12. "최소공배수" +x := 100 ln. "자연 로그" +x := 100 log. "밑이 10인 로그" +x := 100 log: 10. "로그의 바닥" +x := 180 degreesToRadians. "도를 라디안으로 변환" +x := 3.14 radiansToDegrees. "라디안을 도로 변환" +x := 0.7 sin. "사인" +x := 0.7 cos. "코사인" +x := 0.7 tan. "탄젠트" +x := 0.7 arcSin. "아크사인" +x := 0.7 arcCos. "아크코사인" +x := 0.7 arcTan. "아크탄젠트" +x := 10 max: 20. "두 숫자 중 최대값 가져오기" +x := 10 min: 20. "두 숫자 중 최소값 가져오기" +x := Float pi. "파이" +x := Float e. "지수 상수" +x := Float infinity. "무한대" +x := Float nan. "숫자 아님" +x := Random new next; yourself. x next. "난수 스트림 (0.0에서 1.0)" +x := 100 atRandom. "빠른 난수" +``` + +#### 비트 조작: +```smalltalk +| b x | +x := 16rFF bitAnd: 16r0F. "비트 and" +x := 16rF0 bitOr: 16r0F. "비트 or" +x := 16rFF bitXor: 16r0F. "비트 xor" +x := 16rFF bitInvert. "비트 반전" +x := 16r0F bitShift: 4. "왼쪽 시프트" +x := 16rF0 bitShift: -4. "오른쪽 시프트" +"x := 16r80 bitAt: 7." "위치의 비트 (0|1) [!Squeak]" +x := 16r80 highbit. "가장 높은 비트의 위치" +b := 16rFF allMask: 16r0F. "마스크에 설정된 모든 비트가 수신자에 설정되었는지 테스트" +b := 16rFF anyMask: 16r0F. "마스크에 설정된 비트 중 하나라도 수신자에 설정되었는지 테스트" +b := 16rFF noMask: 16r0F. "마스크에 설정된 모든 비트가 수신자에서 지워졌는지 테스트" +``` + +#### 변환: +```smalltalk +| x | +x := 3.99 asInteger. "숫자를 정수로 변환 (Squeak에서는 버림)" +x := 3.99 asFraction. "숫자를 분수로 변환" +x := 3 asFloat. "숫자를 부동 소수점으로 변환" +x := 65 asCharacter. "정수를 문자로 변환" +x := $A asciiValue. "문자를 정수로 변환" +x := 3.99 printString. "printOn:을 통해 객체를 문자열로 변환" +x := 3.99 storeString. "storeOn:을 통해 객체를 문자열로 변환" +x := 15 radix: 16. "지정된 기수로 문자열로 변환" +x := 15 printStringBase: 16. +x := 15 storeStringBase: 16. +``` + +#### 블록: +- 블록은 객체이며 변수에 할당될 수 있습니다. +- 값은 명시적 반환이 없는 한 마지막으로 평가된 표현식입니다. +- 블록은 중첩될 수 있습니다. +- 사양 [ 인수 | | 지역 변수 | 표현식 ] +- Squeak는 현재 블록의 지역 변수를 지원하지 않습니다. +- 최대 세 개의 인수가 허용됩니다. +- `^`표현식은 블록 및 메서드를 종료합니다(모든 중첩 블록 종료). +- 장기 저장을 위한 블록에는 `^`가 포함되어서는 안 됩니다. + +```smalltalk +| x y z | +x := [ y := 1. z := 2. ]. x value. "간단한 블록 사용" +x := [ :argOne :argTwo | argOne, ' and ' , argTwo.]. "인수 전달로 블록 설정" +Transcript show: (x value: 'First' value: 'Second'); cr. "인수 전달로 블록 사용" + +"x := [ | z | z := 1.]. *** Squeak 블록에서는 지역 변수를 사용할 수 없습니다" +``` + +#### 메서드 호출: +- 단항 메서드는 인수가 없는 메시지입니다. +- 이항 메서드 +- 키워드 메서드는 콜론을 포함하는 선택자가 있는 메시지입니다. 표준 범주/프로토콜: +- initialize-release (새 인스턴스에 대해 호출되는 메서드) +- accessing (get/set 메서드) +- testing (불리언 테스트 - is) +- comparing (매개변수가 있는 불리언 테스트) +- displaying (gui 관련 메서드) +- printing (인쇄용 메서드) +- updating (변경 알림 수신) +- private (클래스에 비공개인 메서드) +- instance-creation (인스턴스 생성을 위한 클래스 메서드) + +```smalltalk +| x | +x := 2 sqrt. "단항 메시지" +x := 2 raisedTo: 10. "키워드 메시지" +x := 194 * 9. "이항 메시지" +Transcript show: (194 * 9) printString; cr. "조합 (연쇄)" +x := 2 perform: #sqrt. "간접 메서드 호출" +Transcript "연쇄 - 수신자에게 여러 메시지 보내기" + show: 'hello '; + show: 'world'; + cr. +x := 3 + 2; * 100. "결과=300. 동일한 수신자(3)에게 메시지 보내기" +``` + +#### 조건문: +```smalltalk +| x | +x > 10 ifTrue: [Transcript show: 'ifTrue'; cr]. "if then" +x > 10 ifFalse: [Transcript show: 'ifFalse'; cr]. "if else" + +"if then else" +x > 10 + ifTrue: [Transcript show: 'ifTrue'; cr] + ifFalse: [Transcript show: 'ifFalse'; cr]. + +"if else then" +x > 10 + ifFalse: [Transcript show: 'ifFalse'; cr] + ifTrue: [Transcript show: 'ifTrue'; cr]. +Transcript + show: + (x > 10 + ifTrue: ['ifTrue'] + ifFalse: ['ifFalse']); + cr. + +"중첩된 if then else" +Transcript + show: + (x > 10 + ifTrue: [x > 5 + ifTrue: ['A'] + ifFalse: ['B']] + ifFalse: ['C']); + cr. + +"switch 기능" +switch := Dictionary new. +switch at: $A put: [Transcript show: 'Case A'; cr]. +switch at: $B put: [Transcript show: 'Case B'; cr]. +switch at: $C put: [Transcript show: 'Case C'; cr]. +result := (switch at: $B) value. +``` + +#### 반복문: +```smalltalk +| x y | +x := 4. y := 1. +[x > 0] whileTrue: [x := x - 1. y := y * 2]. "while true 루프" +[x >= 4] whileFalse: [x := x + 1. y := y * 2]. "while false 루프" +x timesRepeat: [y := y * 2]. "times repeat 루프 (i := 1 to x)" +1 to: x do: [:a | y := y * 2]. "for 루프" +1 to: x by: 2 do: [:a | y := y / 2]. "지정된 증분이 있는 for 루프" +#(5 4 3) do: [:a | x := x + a]. "배열 요소를 반복" +``` + +#### 문자: +```smalltalk +| x y | +x := $A. "문자 할당" +y := x isLowercase. "소문자인지 테스트" +y := x isUppercase. "대문자인지 테스트" +y := x isLetter. "문자인지 테스트" +y := x isDigit. "숫자인지 테스트" +y := x isAlphaNumeric. "영숫자인지 테스트" +y := x isSeparator. "구분 문자인지 테스트" +y := x isVowel. "모음인지 테스트" +y := x digitValue. "숫자 값으로 변환" +y := x asLowercase. "소문자로 변환" +y := x asUppercase. "대문자로 변환" +y := x asciiValue. "숫자 ascii 값으로 변환" +y := x asString. "문자열로 변환" +b := $A <= $B. "비교" +y := $A max: $B. +``` + +#### 심볼: +```smalltalk +| b x y | +x := #Hello. "심볼 할당" +y := #Symbol, #Concatenation. "심볼 연결 (결과는 문자열)" +b := x isEmpty. "심볼이 비어 있는지 테스트" +y := x size. "문자열 크기" +y := x at: 2. "위치의 문자" +y := x copyFrom: 2 to: 4. "부분 문자열" +y := x indexOf: $e ifAbsent: [0]. "문자열 내 문자의 첫 번째 위치" +x do: [:a | Transcript show: a printString; cr]. "문자열 반복" +b := x conform: [:a | (a >= $a) & (a <= $z)]. "모든 요소가 조건을 충족하는지 테스트" +y := x select: [:a | a > $a]. "조건을 충족하는 모든 요소 반환" +y := x asString. "심볼을 문자열로 변환" +y := x asText. "심볼을 텍스트로 변환" +y := x asArray. "심볼을 배열로 변환" +y := x asOrderedCollection. "심볼을 순서 있는 컬렉션으로 변환" +y := x asSortedCollection. "심볼을 정렬된 컬렉션으로 변환" +y := x asBag. "심볼을 백 컬렉션으로 변환" +y := x asSet. "심볼을 세트 컬렉션으로 변환" +``` + +#### 문자열: +```smalltalk +| b x y | +x := 'This is a string'. "문자열 할당" +x := 'String', 'Concatenation'. "문자열 연결" +b := x isEmpty. "문자열이 비어 있는지 테스트" +y := x size. "문자열 크기" +y := x at: 2. "위치의 문자" +y := x copyFrom: 2 to: 4. "부분 문자열" +y := x indexOf: $a ifAbsent: [0]. "문자열 내 문자의 첫 번째 위치" +x := String new: 4. "문자열 객체 할당" +x "문자열 요소 설정" + at: 1 put: $a; + at: 2 put: $b; + at: 3 put: $c; + at: 4 put: $e. +x := String with: $a with: $b with: $c with: $d. "한 번에 최대 4개 요소 설정" +x do: [:a | Transcript show: a printString; cr]. "문자열 반복" +b := x conform: [:a | (a >= $a) & (a <= $z)]. "모든 요소가 조건을 충족하는지 테스트" +y := x select: [:a | a > $a]. "조건을 충족하는 모든 요소 반환" +y := x asSymbol. "문자열을 심볼로 변환" +y := x asArray. "문자열을 배열로 변환" +x := 'ABCD' asByteArray. "문자열을 바이트 배열로 변환" +y := x asOrderedCollection. "문자열을 순서 있는 컬렉션으로 변환" +y := x asSortedCollection. "문자열을 정렬된 컬렉션으로 변환" +y := x asBag. "문자열을 백 컬렉션으로 변환" +y := x asSet. "문자열을 세트 컬렉션으로 변환" +y := x shuffled. "문자열 무작위로 섞기" +``` + +#### 배열: +고정 길이 컬렉션 +- ByteArray: 바이트 요소로 제한된 배열 (0-255) +- WordArray: 워드 요소로 제한된 배열 (0-2^32) + +```smalltalk +| b x y z sum max | +x := #(4 3 2 1). "상수 배열" +z := #(1 2 3 'hi'). "혼합 타입 배열" +x := Array with: 5 with: 4 with: 3 with: 2. "최대 4개 요소로 배열 생성" +x := Array new: 4. "지정된 크기로 배열 할당" +x "배열 요소 설정" + at: 1 put: 5; + at: 2 put: 4; + at: 3 put: 3; + at: 4 put: 2. +b := x isEmpty. "배열이 비어 있는지 테스트" +y := x size. "배열 크기" +y := x at: 4. "인덱스에서 배열 요소 가져오기" +b := x includes: 3. "배열에 요소가 있는지 테스트" +y := x copyFrom: 2 to: 4. "부분 배열" +y := x indexOf: 3 ifAbsent: [0]. "배열 내 요소의 첫 번째 위치" +y := x occurrencesOf: 3. "컬렉션에서 객체 수" +x do: [:a | Transcript show: a printString; cr]. "배열 반복" +b := x conform: [:a | (a >= 1) & (a <= 4)]. "모든 요소가 조건을 충족하는지 테스트" +y := x select: [:a | a > 2]. "테스트를 통과하는 요소의 컬렉션 반환" +y := x reject: [:a | a < 2]. "테스트에 실패하는 요소의 컬렉션 반환" +y := x collect: [:a | a + a]. "새 컬렉션을 위해 각 요소 변환" +y := x detect: [:a | a > 3] ifNone: []. "테스트를 통과하는 첫 번째 요소의 위치 찾기" +sum := 0. x do: [:a | sum := sum + a]. sum. "배열 요소 합계" +sum := 0. 1 to: (x size) + do: [:a | sum := sum + (x at: a)]. "배열 요소 합계" +sum := x inject: 0 into: [:a :c | a + c]. "배열 요소 합계" +max := x inject: 0 into: [:a :c | (a > c) "배열에서 최대 요소 찾기" + ifTrue: [a] + ifFalse: [c]]. +y := x shuffled. "컬렉션 무작위로 섞기" +y := x asArray. "배열로 변환" +"y := x asByteArray." "참고: 이 지침은 Squeak에서 사용할 수 없습니다" +y := x asWordArray. "워드 배열로 변환" +y := x asOrderedCollection. "순서 있는 컬렉션으로 변환" +y := x asSortedCollection. "정렬된 컬렉션으로 변환" +y := x asBag. "백 컬렉션으로 변환" +y := x asSet. "세트 컬렉션으로 변환" +``` + +#### OrderedCollection: +확장 가능한 배열처럼 작동합니다 + +```smalltalk +| b x y sum max | +x := OrderedCollection + with: 4 with: 3 with: 2 with: 1. "최대 4개 요소로 컬렉션 생성" +x := OrderedCollection new. "컬렉션 할당" +x add: 3; add: 2; add: 1; add: 4; yourself. "컬렉션에 요소 추가" +y := x addFirst: 5. "컬렉션 시작 부분에 요소 추가" +y := x removeFirst. "컬렉션에서 첫 번째 요소 제거" +y := x addLast: 6. "컬렉션 끝에 요소 추가" +y := x removeLast. "컬렉션에서 마지막 요소 제거" +y := x addAll: #(7 8 9). "컬렉션에 여러 요소 추가" +y := x removeAll: #(7 8 9). "컬렉션에서 여러 요소 제거" +x at: 2 put: 3. "인덱스에 요소 설정" +y := x remove: 5 ifAbsent: []. "컬렉션에서 요소 제거" +b := x isEmpty. "비어 있는지 테스트" +y := x size. "요소 수" +y := x at: 2. "인덱스에서 요소 검색" +y := x first. "컬렉션에서 첫 번째 요소 검색" +y := x last. "컬렉션에서 마지막 요소 검색" +b := x includes: 5. "컬렉션에 요소가 있는지 테스트" +y := x copyFrom: 2 to: 3. "부분 컬렉션" +y := x indexOf: 3 ifAbsent: [0]. "컬렉션 내 요소의 첫 번째 위치" +y := x occurrencesOf: 3. "컬렉션에서 객체 수" +x do: [:a | Transcript show: a printString; cr]. "컬렉션 반복" +b := x conform: [:a | (a >= 1) & (a <= 4)]. "모든 요소가 조건을 충족하는지 테스트" +y := x select: [:a | a > 2]. "테스트를 통과하는 요소의 컬렉션 반환" +y := x reject: [:a | a < 2]. "테스트에 실패하는 요소의 컬렉션 반환" +y := x collect: [:a | a + a]. "새 컬렉션을 위해 각 요소 변환" +y := x detect: [:a | a > 3] ifNone: []. "테스트를 통과하는 첫 번째 요소의 위치 찾기" +sum := 0. x do: [:a | sum := sum + a]. sum. "요소 합계" +sum := 0. 1 to: (x size) + do: [:a | sum := sum + (x at: a)]. "요소 합계" +sum := x inject: 0 into: [:a :c | a + c]. "요소 합계" +max := x inject: 0 into: [:a :c | (a > c) "컬렉션에서 최대 요소 찾기" + ifTrue: [a] + ifFalse: [c]]. +y := x shuffled. "컬렉션 무작위로 섞기" +y := x asArray. "배열로 변환" +y := x asOrderedCollection. "순서 있는 컬렉션으로 변환" +y := x asSortedCollection. "정렬된 컬렉션으로 변환" +y := x asBag. "백 컬렉션으로 변환" +y := x asSet. "세트 컬렉션으로 변환" +``` + +#### SortedCollection: +요소 순서가 정렬 기준에 따라 결정된다는 점을 제외하고 OrderedCollection과 같습니다. + +```smalltalk +| b x y sum max | +x := SortedCollection + with: 4 with: 3 with: 2 with: 1. "최대 4개 요소로 컬렉션 생성" +x := SortedCollection new. "컬렉션 할당" +x := SortedCollection sortBlock: [:a :c | a > c]. "정렬 기준 설정" +x add: 3; add: 2; add: 1; add: 4; yourself. "컬렉션에 요소 추가" +y := x addFirst: 5. "컬렉션 시작 부분에 요소 추가" +y := x removeFirst. "컬렉션에서 첫 번째 요소 제거" +y := x addLast: 6. "컬렉션 끝에 요소 추가" +y := x removeLast. "컬렉션에서 마지막 요소 제거" +y := x addAll: #(7 8 9). "컬렉션에 여러 요소 추가" +y := x removeAll: #(7 8 9). "컬렉션에서 여러 요소 제거" +y := x remove: 5 ifAbsent: []. "컬렉션에서 요소 제거" +b := x isEmpty. "비어 있는지 테스트" +y := x size. "요소 수" +y := x at: 2. "인덱스에서 요소 검색" +y := x first. "컬렉션에서 첫 번째 요소 검색" +y := x last. "컬렉션에서 마지막 요소 검색" +b := x includes: 4. "컬렉션에 요소가 있는지 테스트" +y := x copyFrom: 2 to: 3. "부분 컬렉션" +y := x indexOf: 3 ifAbsent: [0]. "컬렉션 내 요소의 첫 번째 위치" +y := x occurrencesOf: 3. "컬렉션에서 객체 수" +x do: [:a | Transcript show: a printString; cr]. "컬렉션 반복" +b := x conform: [:a | (a >= 1) & (a <= 4)]. "모든 요소가 조건을 충족하는지 테스트" +y := x select: [:a | a > 2]. "테스트를 통과하는 요소의 컬렉션 반환" +y := x reject: [:a | a < 2]. "테스트에 실패하는 요소의 컬렉션 반환" +y := x collect: [:a | a + a]. "새 컬렉션을 위해 각 요소 변환" +y := x detect: [:a | a > 3] ifNone: []. "테스트를 통과하는 첫 번째 요소의 위치 찾기" +sum := 0. x do: [:a | sum := sum + a]. sum. "요소 합계" +sum := 0. 1 to: (x size) + do: [:a | sum := sum + (x at: a)]. "요소 합계" +sum := x inject: 0 into: [:a :c | a + c]. "요소 합계" +max := x inject: 0 into: [:a :c | (a > c) "컬렉션에서 최대 요소 찾기" + ifTrue: [a] + ifFalse: [c]]. +y := x asArray. "배열로 변환" +y := x asOrderedCollection. "순서 있는 컬렉션으로 변환" +y := x asSortedCollection. "정렬된 컬렉션으로 변환" +y := x asBag. "백 컬렉션으로 변환" +y := x asSet. "세트 컬렉션으로 변환" +``` + +#### Bag: +요소가 특정 순서가 아니라는 점을 제외하고 OrderedCollection과 같습니다. + +```smalltalk +| b x y sum max | +x := Bag with: 4 with: 3 with: 2 with: 1. "최대 4개 요소로 컬렉션 생성" +x := Bag new. "컬렉션 할당" +x add: 4; add: 3; add: 1; add: 2; yourself. "컬렉션에 요소 추가" +x add: 3 withOccurrences: 2. "컬렉션에 여러 복사본 추가" +y := x addAll: #(7 8 9). "컬렉션에 여러 요소 추가" +y := x removeAll: #(7 8 9). "컬렉션에서 여러 요소 제거" +y := x remove: 4 ifAbsent: []. "컬렉션에서 요소 제거" +b := x isEmpty. "비어 있는지 테스트" +y := x size. "요소 수" +b := x includes: 3. "컬렉션에 요소가 있는지 테스트" +y := x occurrencesOf: 3. "컬렉션에서 객체 수" +x do: [:a | Transcript show: a printString; cr]. "컬렉션 반복" +b := x conform: [:a | (a >= 1) & (a <= 4)]. "모든 요소가 조건을 충족하는지 테스트" +y := x select: [:a | a > 2]. "테스트를 통과하는 요소의 컬렉션 반환" +y := x reject: [:a | a < 2]. "테스트에 실패하는 요소의 컬렉션 반환" +y := x collect: [:a | a + a]. "새 컬렉션을 위해 각 요소 변환" +y := x detect: [:a | a > 3] ifNone: []. "테스트를 통과하는 첫 번째 요소의 위치 찾기" +sum := 0. x do: [:a | sum := sum + a]. sum. "요소 합계" +sum := x inject: 0 into: [:a :c | a + c]. "요소 합계" +max := x inject: 0 into: [:a :c | (a > c) "컬렉션에서 최대 요소 찾기" + ifTrue: [a] + ifFalse: [c]]. +y := x asOrderedCollection. "순서 있는 컬렉션으로 변환" +y := x asSortedCollection. "정렬된 컬렉션으로 변환" +y := x asBag. "백 컬렉션으로 변환" +y := x asSet. "세트 컬렉션으로 변환" +``` + +#### Set: +중복이 허용되지 않는다는 점을 제외하고 Bag과 같습니다. + +#### IdentitySet: +동일성 테스트를 사용합니다(== 대신 =) + +```smalltalk +| b x y sum max | +x := Set with: 4 with: 3 with: 2 with: 1. "최대 4개 요소로 컬렉션 생성" +x := Set new. "컬렉션 할당" +x add: 4; add: 3; add: 1; add: 2; yourself. "컬렉션에 요소 추가" +y := x addAll: #(7 8 9). "컬렉션에 여러 요소 추가" +y := x removeAll: #(7 8 9). "컬렉션에서 여러 요소 제거" +y := x remove: 4 ifAbsent: []. "컬렉션에서 요소 제거" +b := x isEmpty. "비어 있는지 테스트" +y := x size. "요소 수" +x includes: 4. "컬렉션에 요소가 있는지 테스트" +x do: [:a | Transcript show: a printString; cr]. "컬렉션 반복" +b := x conform: [:a | (a >= 1) & (a <= 4)]. "모든 요소가 조건을 충족하는지 테스트" +y := x select: [:a | a > 2]. "테스트를 통과하는 요소의 컬렉션 반환" +y := x reject: [:a | a < 2]. "테스트에 실패하는 요소의 컬렉션 반환" +y := x collect: [:a | a + a]. "새 컬렉션을 위해 각 요소 변환" +y := x detect: [:a | a > 3] ifNone: []. "테스트를 통과하는 첫 번째 요소의 위치 찾기" +sum := 0. x do: [:a | sum := sum + a]. sum. "요소 합계" +sum := x inject: 0 into: [:a :c | a + c]. "요소 합계" +max := x inject: 0 into: [:a :c | (a > c) "컬렉션에서 최대 요소 찾기" + ifTrue: [a] + ifFalse: [c]]. +y := x asArray. "배열로 변환" +y := x asOrderedCollection. "순서 있는 컬렉션으로 변환" +y := x asSortedCollection. "정렬된 컬렉션으로 변환" +y := x asBag. "백 컬렉션으로 변환" +y := x asSet. "세트 컬렉션으로 변환" +``` + +#### Interval: +```smalltalk +| b x y sum max | +x := Interval from: 5 to: 10. "간격 객체 생성" +x := 5 to: 10. +x := Interval from: 5 to: 10 by: 2. "지정된 증분이 있는 간격 객체 생성" +x := 5 to: 10 by: 2. +b := x isEmpty. "비어 있는지 테스트" +y := x size. "요소 수" +x includes: 9. "컬렉션에 요소가 있는지 테스트" +x do: [:k | Transcript show: k printString; cr]. "간격 반복" +b := x conform: [:a | (a >= 1) & (a <= 4)]. "모든 요소가 조건을 충족하는지 테스트" +y := x select: [:a | a > 7]. "테스트를 통과하는 요소의 컬렉션 반환" +y := x reject: [:a | a < 2]. "테스트에 실패하는 요소의 컬렉션 반환" +y := x collect: [:a | a + a]. "새 컬렉션을 위해 각 요소 변환" +y := x detect: [:a | a > 3] ifNone: []. "테스트를 통과하는 첫 번째 요소의 위치 찾기" +sum := 0. x do: [:a | sum := sum + a]. sum. "요소 합계" +sum := 0. 1 to: (x size) + do: [:a | sum := sum + (x at: a)]. "요소 합계" +sum := x inject: 0 into: [:a :c | a + c]. "요소 합계" +max := x inject: 0 into: [:a :c | (a > c) "컬렉션에서 최대 요소 찾기" + ifTrue: [a] + ifFalse: [c]]. +y := x asArray. "배열로 변환" +y := x asOrderedCollection. "순서 있는 컬렉션으로 변환" +y := x asSortedCollection. "정렬된 컬렉션으로 변환" +y := x asBag. "백 컬렉션으로 변환" +y := x asSet. "세트 컬렉션으로 변환" +``` + +#### 연관: +```smalltalk +| x y | +x := #myVar->'hello'. +y := x key. +y := x value. +``` + +#### 딕셔너리: +#### IdentityDictionary: +동일성 테스트를 사용합니다(== 대신 =) + +```smalltalk +| b x y | +x := Dictionary new. "컬렉션 할당" +x add: #a->4; + add: #b->3; + add: #c->1; + add: #d->2; yourself. "컬렉션에 요소 추가" +x at: #e put: 3. "인덱스에 요소 설정" +b := x isEmpty. "비어 있는지 테스트" +y := x size. "요소 수" +y := x at: #a ifAbsent: []. "인덱스에서 요소 검색" +y := x keyAtValue: 3 ifAbsent: []. "오류 블록이 있는 주어진 값에 대한 키 검색" +y := x removeKey: #e ifAbsent: []. "컬렉션에서 요소 제거" +b := x includes: 3. "값 컬렉션에 요소가 있는지 테스트" +b := x includesKey: #a. "키 컬렉션에 요소가 있는지 테스트" +y := x occurrencesOf: 3. "컬렉션에서 객체 수" +y := x keys. "키 집합" +y := x values. "값 백" +x do: [:a | Transcript show: a printString; cr]. "값 컬렉션 반복" +x keysDo: [:a | Transcript show: a printString; cr]. "키 컬렉션 반복" +x associationsDo: [:a | Transcript show: a printString; cr]."연관 반복" +x keysAndValuesDo: [:aKey :aValue | Transcript "키와 값 반복" + show: aKey printString; space; + show: aValue printString; cr]. +b := x conform: [:a | (a >= 1) & (a <= 4)]. "모든 요소가 조건을 충족하는지 테스트" +y := x select: [:a | a > 2]. "테스트를 통과하는 요소의 컬렉션 반환" +y := x reject: [:a | a < 2]. "테스트에 실패하는 요소의 컬렉션 반환" +y := x collect: [:a | a + a]. "새 컬렉션을 위해 각 요소 변환" +y := x detect: [:a | a > 3] ifNone: []. "테스트를 통과하는 첫 번째 요소의 위치 찾기" +sum := 0. x do: [:a | sum := sum + a]. sum. "요소 합계" +sum := x inject: 0 into: [:a :c | a + c]. "요소 합계" +max := x inject: 0 into: [:a :c | (a > c) "컬렉션에서 최대 요소 찾기" + ifTrue: [a] + ifFalse: [c]]. +y := x asArray. "배열로 변환" +y := x asOrderedCollection. "순서 있는 컬렉션으로 변환" +y := x asSortedCollection. "정렬된 컬렉션으로 변환" +y := x asBag. "백 컬렉션으로 변환" +y := x asSet. "세트 컬렉션으로 변환" + +Smalltalk at: #CMRGlobal put: 'CMR entry'. "Smalltalk 딕셔너리에 전역 넣기" +x := Smalltalk at: #CMRGlobal. "Smalltalk 딕셔너리에서 전역 읽기" +Transcript show: (CMRGlobal printString). "항목은 이름으로 직접 액세스 가능" +Smalltalk keys do: [ :k | "모든 클래스 출력" + ((Smalltalk at: k) isKindOf: Class) + ifFalse: [Transcript show: k printString; cr]]. +Smalltalk at: #CMRDictionary put: (Dictionary new). "사용자 정의 딕셔너리 설정" +CMRDictionary at: #MyVar1 put: 'hello1'. "딕셔너리에 항목 넣기" +CMRDictionary add: #MyVar2->'hello2'. "키->값 조합을 사용하여 딕셔너리에 항목 추가" +CMRDictionary size. "딕셔너리 크기" +CMRDictionary keys do: [ :k | "딕셔너리에서 키 출력" + Transcript show: k printString; cr]. +CMRDictionary values do: [ :k | "딕셔너리에서 값 출력" + Transcript show: k printString; cr]. +CMRDictionary keysAndValuesDo: [:aKey :aValue | "키와 값 출력" + Transcript + show: aKey printString; + space; + show: aValue printString; + cr]. +CMRDictionary associationsDo: [:aKeyValue | "키 값 인쇄를 위한 또 다른 반복자" + Transcript show: aKeyValue printString; cr]. +Smalltalk removeKey: #CMRGlobal ifAbsent: []. "Smalltalk 딕셔너리에서 항목 제거" +Smalltalk removeKey: #CMRDictionary ifAbsent: []. "Smalltalk 딕셔너리에서 사용자 딕셔너리 제거" +``` + +#### 내부 스트림: +```smalltalk +| b x ios | +ios := ReadStream on: 'Hello read stream'. +ios := ReadStream on: 'Hello read stream' from: 1 to: 5. +[(x := ios nextLine) notNil] whileTrue: [Transcript show: x; cr]. +ios position: 3. +ios position. +x := ios next. +x := ios peek. +x := ios contents. +b := ios atEnd. + +ios := ReadWriteStream on: 'Hello read stream'. +ios := ReadWriteStream on: 'Hello read stream' from: 1 to: 5. +ios := ReadWriteStream with: 'Hello read stream'. +ios := ReadWriteStream with: 'Hello read stream' from: 1 to: 10. +ios position: 0. +[(x := ios nextLine) notNil] whileTrue: [Transcript show: x; cr]. +ios position: 6. +ios position. +ios nextPutAll: 'Chris'. +x := ios next. +x := ios peek. +x := ios contents. +b := ios atEnd. +``` + +#### 파일 스트림: +```smalltalk +| b x ios | +ios := FileStream newFileNamed: 'ios.txt'. +ios nextPut: $H; cr. +ios nextPutAll: 'Hello File'; cr. +'Hello File' printOn: ios. +'Hello File' storeOn: ios. +ios close. + +ios := FileStream oldFileNamed: 'ios.txt'. +[(x := ios nextLine) notNil] whileTrue: [Transcript show: x; cr]. +ios position: 3. +x := ios position. +x := ios next. +x := ios peek. +b := ios atEnd. +ios close. +``` + +#### 날짜: +```smalltalk +| x y | +x := Date today. "오늘 날짜 생성" +x := Date dateAndTimeNow. "현재 시간/날짜로 날짜 생성" +x := Date readFromString: '01/02/1999'. "형식화된 문자열에서 날짜 생성" +x := Date newDay: 12 month: #July year: 1999 "부분에서 날짜 생성" +x := Date fromDays: 36000. "1901년 1월 1일 이후 경과 일수에서 날짜 생성" +y := Date dayOfWeek: #Monday. "요일을 정수로 (1-7)" +y := Date indexOfMonth: #January. "연중 월을 정수로 (1-12)" +y := Date daysInMonth: 2 forYear: 1996. "월의 일을 정수로 (1-31)" +y := Date daysInYear: 1996. "연중 일수 (365|366)" +y := Date nameOfDay: 1 "요일 이름 (#Monday,...)" +y := Date nameOfMonth: 1. "월 이름 (#January,...)" +y := Date leapYear: 1996. "윤년이면 1; 아니면 0" +y := x weekday. "요일 (#Monday,...)" +y := x previous: #Monday. "이전 요일의 날짜" +y := x dayOfMonth. "월의 일 (1-31)" +y := x day. "연중 일 (1-366)" +y := x firstDayOfMonth. "월의 첫째 날에 대한 연중 일" +y := x monthName. "연중 월 (#January,...)" +y := x monthIndex. "연중 월 (1-12)" +y := x daysInMonth. "월의 일수 (1-31)" +y := x year. "연도 (19xx)" +y := x daysInYear. "연중 일수 (365|366)" +y := x daysLeftInYear. "연중 남은 일수 (364|365)" +y := x asSeconds. "1901년 1월 1일 이후 경과 초" +y := x addDays: 10. "날짜 객체에 일 추가" +y := x subtractDays: 10. "날짜 객체에서 일 빼기" +y := x subtractDate: (Date today). "날짜 빼기 (결과는 일 단위)" +y := x printFormat: #(2 1 3 $/ 1 1). "형식화된 날짜 인쇄" +b := (x <= Date today). "비교" +``` + +#### 시간: +```smalltalk +| x y | +x := Time now. "현재 시간으로 시간 생성" +x := Time dateAndTimeNow. "현재 시간/날짜로 시간 생성" +x := Time readFromString: '3:47:26 pm'. "형식화된 문자열에서 시간 생성" +x := Time fromSeconds: (60 * 60 * 4). "자정 이후 경과 시간으로 시간 생성" +y := Time millisecondClockValue. "자정 이후 밀리초" +y := Time totalSeconds. "1901년 1월 1일 이후 총 초" +y := x seconds. "분을 지난 초 (0-59)" +y := x minutes. "시간을 지난 분 (0-59)" +y := x hours. "자정을 지난 시간 (0-23)" +y := x addTime: (Time now). "시간 객체에 시간 추가" +y := x subtractTime: (Time now). "시간 객체에서 시간 빼기" +y := x asSeconds. "시간을 초로 변환" +x := Time millisecondsToRun: [ "타이밍 기능" + 1 to: 1000 do: [:index | y := 3.14 * index]]. +b := (x <= Time now). "비교" +``` + +#### 점: +```smalltalk +| x y | +x := 200@100. "새 점 얻기" +y := x x. "x 좌표" +y := x y. "y 좌표" +x := 200@100 negated. "x와 y 부정" +x := (-200@-100) abs. "x와 y의 절대값" +x := (200.5@100.5) rounded. "x와 y 반올림" +x := (200.5@100.5) truncated. "x와 y 버림" +x := 200@100 + 100. "x와 y에 스케일 추가" +x := 200@100 - 100. "x와 y에서 스케일 빼기" +x := 200@100 * 2. "x와 y에 스케일 곱하기" +x := 200@100 / 2. "x와 y를 스케일로 나누기" +x := 200@100 // 2. "x와 y를 스케일로 나누기" +x := 200@100 \\ 3. "x와 y를 스케일로 나눈 나머지" +x := 200@100 + 50@25. "점 더하기" +x := 200@100 - 50@25. "점 빼기" +x := 200@100 * 3@4. "점 곱하기" +x := 200@100 // 3@4. "점 나누기" +x := 200@100 max: 50@200. "x와 y의 최대값" +x := 200@100 min: 50@200. "x와 y의 최소값" +x := 20@5 dotProduct: 10@2. "곱의 합 (x1*x2 + y1*y2)" +``` + +#### 사각형: +```smalltalk +Rectangle fromUser. +``` + +#### 펜: +```smalltalk +| myPen | +Display restoreAfter: [ + Display fillWhite. + +myPen := Pen new. "그래픽 펜 얻기" +myPen squareNib: 1. +myPen color: (Color blue). "펜 색상 설정" +myPen home. "디스플레이 중앙에 펜 위치" +myPen up. "펜촉을 그릴 수 없게 만듦" +myPen down. "펜촉을 그릴 수 있게 함" +myPen north. "방향을 위쪽으로 가리킴" +myPen turn: -180. "방향에 지정된 각도 추가" +myPen direction. "펜의 현재 각도 가져오기" +myPen go: 50. "지정된 픽셀 수만큼 펜 이동" +myPen location. "펜 위치 가져오기" +myPen goto: 200@200. "지정된 지점으로 이동" +myPen place: 250@250. "그리지 않고 지정된 지점으로 이동" +myPen print: 'Hello World' + withFont: (TextStyle default fontAt: 1). +Display extent. "디스플레이 너비@높이 가져오기" +Display width. "디스플레이 너비 가져오기" +Display height. "디스플레이 높이 가져오기" + +]. +``` + +#### 동적 메시지 호출/컴파일: +```smalltalk +| receiver message result argument keyword1 keyword2 argument1 argument2 | + +"단항 메시지" +receiver := 5. +message := 'factorial' asSymbol. +result := receiver perform: message. +result := Compiler evaluate: ((receiver storeString), ' ', message). +result := (Message new setSelector: message arguments: #()) sentTo: receiver. + +"이항 메시지" +receiver := 1. +message := '+' asSymbol. +argument := 2. +result := receiver perform: message withArguments: (Array with: argument). +result := Compiler evaluate: ((receiver storeString), ' ', message, ' ', (argument storeString)). +result := (Message new setSelector: message arguments: (Array with: argument)) sentTo: receiver. + +"키워드 메시지" +receiver := 12. +keyword1 := 'between:' asSymbol. +keyword2 := 'and:' asSymbol. +argument1 := 10. +argument2 := 20. + +result := receiver + perform: (keyword1, keyword2) asSymbol + withArguments: (Array with: argument1 with: argument2). + +result := Compiler evaluate: + ((receiver storeString), ' ', keyword1, (argument1 storeString) , ' ', keyword2, (argument2 storeString)). + +result := (Message + new + setSelector: (keyword1, keyword2) asSymbol + arguments: (Array with: argument1 with: argument2)) + sentTo: receiver. +``` + +#### 클래스/메타 클래스: +```smalltalk +| b x | +x := String name. "클래스 이름" +x := String category. "조직 범주" +x := String comment. "클래스 주석" +x := String kindOfSubclass. "하위 클래스 타입 - subclass: variableSubclass 등" +x := String definition. "클래스 정의" +x := String instVarNames. "직접 인스턴스 변수 이름" +x := String allInstVarNames. "누적 인스턴스 변수 이름" +x := String classVarNames. "직접 클래스 변수 이름" +x := String allClassVarNames. "누적 클래스 변수 이름" +x := String sharedPools. "공유 풀로 사용되는 직접 딕셔너리" +x := String allSharedPools. "공유 풀로 사용되는 누적 딕셔너리" +x := String selectors. "클래스에 대한 메시지 선택자" +x := String sourceCodeAt: #size. "지정된 메서드의 소스 코드" +x := String allInstances. "클래스의 모든 인스턴스 컬렉션" +x := String superclass. "직접 상위 클래스" +x := String allSuperclasses. "누적 상위 클래스" +x := String withAllSuperclasses. "수신자 클래스 및 누적 상위 클래스" +x := String subclasses. "직접 하위 클래스" +x := String allSubclasses. "누적 하위 클래스" +x := String withAllSubclasses. "수신자 클래스 및 누적 하위 클래스" +b := String instSize. "명명된 인스턴스 변수 수" +b := String isFixed. "인덱싱된 인스턴스 변수가 없으면 true" +b := String isVariable. "인덱싱된 인스턴스 변수가 있으면 true" +b := String isPointers. "인덱스 인스턴스 변수에 객체가 포함되어 있으면 true" +b := String isBits. "인덱스 인스턴스 변수에 바이트/워드가 포함되어 있으면 true" +b := String isBytes. "인덱스 인스턴스 변수에 바이트가 포함되어 있으면 true" +b := String isWords. "인덱스 인스턴스 변수에 워드가 포함되어 있으면 true" +Object withAllSubclasses size. "총 클래스 항목 수 가져오기" +``` + +#### 디버깅: +```smalltalk +| a b x | +x yourself. "수신자 반환" +String browse. "지정된 클래스 탐색" +x inspect. "객체 검사기 창 열기" +x confirm: 'Is this correct?'. +x halt. "디버거 창을 열기 위한 중단점" +x halt: 'Halt message'. +x notify: 'Notify text'. +x error: 'Error string'. "제목이 있는 오류 창 열기" +x doesNotUnderstand: #cmrMessage. "메시지가 처리되지 않음을 플래그" +x shouldNotImplement. "메시지가 구현되어서는 안 됨을 플래그" +x subclassResponsibility. "메시지를 추상으로 플래그" +x errorImproperStore. "인덱싱 가능한 객체에 대한 부적절한 저장을 플래그" +x errorNonIntegerIndex. "정수만 인덱스로 사용해야 함을 플래그" +x errorSubscriptBounds. "경계 밖의 첨자를 플래그" +x primitiveFailed. "시스템 기본 실패" + +a := 'A1'. b := 'B2'. a become: b. "두 객체 교환" +Transcript show: a, b; cr. +``` + +#### 기타 +```smalltalk +| x | +x := 1.2 hash. "객체에 대한 해시 값" +y := x copy. "객체 복사" +y := x shallowCopy. "객체 복사 (재정의되지 않음)" +y := x deepCopy. "객체 및 인스턴스 변수 복사" +y := x veryDeepCopy. "딕셔너리를 사용한 전체 트리 복사" +"Smalltalk condenseChanges." "변경 파일 압축" +x := FillInTheBlank request: 'Prompt Me'. "사용자에게 입력 프롬프트" +Utilities openCommandKeyHelp +``` + +## 더 알아보기 + +### 온라인 Smalltalk 시스템 +대부분의 Smalltalk는 OSS로 무료이거나 상업적 사용에 일부 지불이 필요한 무료 다운로드 버전이 있습니다. +* [Squeak](https://www.squeak.org) +* [Pharo](http://pharo.org) +* [Smalltalk/X](https://www.exept.de/en/smalltalk-x.html) +* [Gemstone](http://gemtalksystems.com/) +* [VA Smalltalk](http://www.instantiations.com/products/vasmalltalk/) +* [VisualWorks Smalltalk](http://www.cincomsmalltalk.com/) + +### 온라인 Smalltalk 책 및 기사 +* [Smalltalk 치트 시트](http://www.angelfire.com/tx4/cus/notes/smalltalk.html) +* [Smalltalk-72 매뉴얼](http://www.bitsavers.org/pdf/xerox/parc/techReports/Smalltalk-72_Instruction_Manual_Mar76.pdf) +* [GNU Smalltalk 사용자 가이드](https://www.gnu.org/software/smalltalk/manual/html_node/Tutorial.html) + +#### 역사적 문서 +* [BYTE: Smalltalk 특별호](https://archive.org/details/byte-magazine-1981-08) +* [Smalltalk-72 매뉴얼](http://www.bitsavers.org/pdf/xerox/parc/techReports/Smalltalk-72_Instruction_Manual_Mar76.pdf) +* [Smalltalk, 객체 및 디자인](https://books.google.co.in/books?id=W8_Une9cbbgC&printsec=frontcover&dq=smalltalk&hl=en&sa=X&ved=0CCIQ6AEwAWoVChMIw63Vo6CpyAIV0HGOCh3S2Alf#v=onepage&q=smalltalk&f=false) +* [Smalltalk: VisualWorks를 사용한 애플리케이션 개발 소개](https://books.google.co.in/books?id=zalQAAAAMAAJ&q=smalltalk&dq=smalltalk&hl=en&sa=X&ved=0CCgQ6AEwAmoVChMIw63Vo6CpyAIV0HGOCh3S2Alf/) diff --git a/ko/solidity.md b/ko/solidity.md new file mode 100644 index 0000000000..e946789c6f --- /dev/null +++ b/ko/solidity.md @@ -0,0 +1,952 @@ +--- +name: Solidity +filename: learnSolidity.sol +contributors: + - ["Nemil Dalal", "https://www.nemil.com"] + - ["Joseph Chow", ""] + - ["Bhoomtawath Plinsut", "https://github.com/varshard"] + - ["Shooter", "https://github.com/liushooter"] + - ["Patrick Collins", "https://gist.github.com/PatrickAlphaC"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Solidity를 사용하면 중앙 집중식 또는 신뢰할 수 있는 당사자 없이 스마트 계약을 생성하고 실행할 수 있는 블록체인 기반 가상 머신인 [Ethereum](https://www.ethereum.org/)에서 프로그래밍할 수 있습니다. + +Solidity는 JavaScript 및 C와 유사한 정적으로 유형이 지정된 계약 프로그래밍 언어입니다. OOP의 객체와 마찬가지로 각 계약에는 상태 변수, 함수 및 공통 데이터 유형이 포함됩니다. 계약별 기능에는 수정자(가드) 절, 리스너용 이벤트 알리미 및 사용자 지정 전역 변수가 포함됩니다. + +일부 이더리움 계약 예로는 크라우드펀딩, 투표, [탈중앙화 금융](https://defipulse.com/) 및 블라인드 경매가 있습니다. + +Solidity 코드에는 오류에 대한 높은 위험과 높은 비용이 있으므로 테스트하고 천천히 출시하는 데 매우 신중해야 합니다. 이더리움의 급격한 변화로 인해 이 문서는 최신 상태를 유지하기 어려울 수 있으므로 최신 정보는 SOLIDITY 채팅방과 이더리움 블로그를 따르는 것이 좋습니다. 여기의 모든 코드는 오류나 오래된 코드 패턴의 상당한 위험과 함께 있는 그대로 제공됩니다. + +다른 코드와 달리 위험을 줄이기 위해 일시 중지, 사용 중단 및 사용량 조절과 같은 디자인 패턴을 추가해야 할 수도 있습니다. 이 문서는 주로 구문을 다루므로 많은 인기 있는 디자인 패턴은 제외합니다. + +Solidity와 이더리움은 활발하게 개발 중이므로 실험적이거나 베타 기능은 일반적으로 표시되며 변경될 수 있습니다. 풀 리퀘스트를 환영합니다. + +# Remix 및 Metamask 작업 + +솔리디티 코드를 빌드, 배포 및 테스트하는 가장 쉬운 방법 중 하나는 다음을 사용하는 것입니다. + +1. [Remix 웹 IDE](https://remix.ethereum.org/) +2. [메타마스크 지갑](https://metamask.io/). + +시작하려면 [메타마스크 브라우저 확장 프로그램 다운로드](https://metamask.io/)를 받으십시오. + +설치가 완료되면 Remix로 작업합니다. 아래 코드가 미리 로드되지만, 그곳으로 가기 전에 Remix를 시작하기 위한 몇 가지 팁을 살펴보겠습니다. [이 링크를 눌러](https://remix.ethereum.org/#version=soljson-v0.8.19+commit.7dd6d404.js&optimize=false&evmVersion=null&gist=2acb47a0286fe45d2464fa937f00fef3&runs=200) 모두 로드하십시오. + +1. 솔리디티 컴파일러 선택 + +![Solidity-in-remix](/images/solidity/remix-solidity.png) + +2. 해당 링크로 로드된 파일 열기 + +![Solidity-choose-file](/images/solidity/remix-choose-file.png) + +3. 파일 컴파일 + +![Solidity-compile](/images/solidity/remix-compile.png) + +4. 배포 + +![Solidity-deploy](/images/solidity/remix-deploy.png) + +5. 계약과 놀기 + +![Solidity-deploy](/images/solidity/remix-interact.png) + +첫 번째 계약을 배포했습니다! 축하합니다! + +정의된 함수를 테스트하고 놀 수 있습니다. 각 함수가 무엇을 하는지 알아보려면 주석을 확인하십시오. + +지금은 달리 지시하지 않는 한 `Remix VM`을 계속 사용하십시오. + + +```solidity +// 먼저, 간단한 은행 계약 +// 입금, 출금 및 잔액 조회를 허용합니다. + +// simple_bank.sol (.sol 확장자 참고) +/* **** 예제 시작 **** */ +// 파일 상단에 특수 주석을 사용하여 +// 코드 라이선스와 사용된 솔리디티 버전을 나타냅니다. +// SPDX-License-Identifier: MIT + +// 소스 파일 컴파일러 버전 선언 +pragma solidity ^0.8.19; + +// Natspec 주석(세 개의 슬래시)으로 시작 +// 문서화 및 UI 요소/작업에 대한 설명 데이터로 사용됩니다. + +/// @title SimpleBank +/// @author nemild + +/* 'contract'는 다른 언어의 'class'와 유사합니다(클래스 변수, +상속 등). */ +contract SimpleBank { // 대문자 단어 + // 함수 외부에서 상태 변수를 선언하면 계약 수명 동안 지속됩니다. + + // 주소를 잔액에 매핑하는 사전 + mapping (address => uint) private balances; + + // "private"은 다른 계약이 잔액을 직접 쿼리할 수 없음을 의미합니다. + // 하지만 데이터는 블록체인의 다른 당사자에게 여전히 볼 수 있습니다. + + address public owner; + // 'public'은 사용자나 계약이 외부에서 읽을 수 있도록 합니다(쓰기 불가). + + // 이벤트 - 외부 리스너에게 작업을 알립니다. + event LogDepositMade(address accountAddress, uint amount); + + // 생성자, 여기에서 하나 또는 여러 변수를 받을 수 있습니다. 하나만 허용됩니다. + constructor() { + // msg는 계약으로 전송된 메시지에 대한 세부 정보를 제공합니다. + // msg.sender는 계약 호출자입니다(계약 생성자의 주소). + owner = msg.sender; + } + + /// @notice 은행에 이더 입금 + /// @return 입금 후 사용자 잔액 + function deposit() public payable returns (uint) { + // 'require'를 사용하여 사용자 입력을 테스트하고, 'assert'를 사용하여 내부 불변량을 테스트합니다. + // 여기서는 오버플로 문제가 없는지 확인합니다. + // 최신 버전의 솔리디티에서는 자동으로 확인됩니다. + require((balances[msg.sender] + msg.value) >= balances[msg.sender]); + + balances[msg.sender] += msg.value; + // 상태 변수에는 "this." 또는 "self."가 필요하지 않습니다. + // 모든 값은 기본적으로 데이터 유형의 초기 값으로 설정됩니다. + + emit LogDepositMade(msg.sender, msg.value); // 이벤트 발생 + + return balances[msg.sender]; + } + + /// @notice 은행에서 이더 인출 + /// @dev 전송된 초과 이더는 반환하지 않습니다. + /// @param withdrawAmount 인출하려는 금액 + /// @return remainingBal + function withdraw(uint withdrawAmount) public returns (uint remainingBal) { + require(withdrawAmount <= balances[msg.sender]); + + // 보내기 전에 즉시 잔액을 공제하는 방식에 유의하십시오. + // 이 계약에서 모든 .transfer/.send는 외부 함수를 호출할 수 있습니다. + // 이로 인해 호출자가 재귀 호출을 사용하여 잔액보다 + // 많은 금액을 요청할 수 있습니다. + // .transfer/.send를 포함한 외부 함수를 호출하기 전에 상태를 커밋하는 것을 목표로 하십시오. + balances[msg.sender] -= withdrawAmount; + + // 이것은 실패 시 자동으로 예외를 발생시키므로 업데이트된 잔액이 되돌려집니다. + payable(msg.sender).transfer(withdrawAmount); + + return balances[msg.sender]; + } + + /// @notice 잔액 조회 + /// @return 사용자 잔액 + // 'view'(예: 상수)는 함수가 상태 변수를 편집하는 것을 방지합니다. + // 함수가 로컬/블록체인 외부에서 실행되도록 허용합니다. + function balance() view public returns (uint) { + return balances[msg.sender]; + } +} +// ** 예제 끝 ** + + +// 이제 솔리디티의 기본 사항 + +// 1. 데이터 유형 및 관련 메서드 +// uint는 통화 금액(더블 또는 플로트 없음)과 +// 날짜(유닉스 시간)에 사용됩니다. +uint x; + +// 256비트 int, 인스턴스화 후 변경할 수 없음 +int constant a = 8; +int256 constant a = 8; // 위 줄과 동일한 효과, 여기서는 256이 명시적임 +uint constant VERSION_ID = 0x123A1; // 16진수 상수 +// 'constant'를 사용하면 컴파일러가 각 발생을 실제 값으로 바꿉니다. + +// 모든 상태 변수(함수 외부의 변수)는 +// 기본적으로 'internal'이며 계약 내부 및 +// 상속하는 모든 계약에서만 액세스할 수 있습니다. +// 외부 계약이 액세스할 수 있도록 하려면 명시적으로 'public'으로 설정해야 합니다. +int256 public a = 8; + +// int 및 uint의 경우 256까지 8단계로 공간을 명시적으로 설정할 수 있습니다. +// 예: int8, int16, int24 +uint8 b; +int64 c; +uint248 e; + +// 이전 버전의 솔리디티에서는 덧셈으로 인해 "오버플로"가 발생할 수 있었습니다. +// 예를 들어, 덧셈의 경우 다음과 같이 수행합니다. +uint256 c = a + b; +assert(c >= a); +// 하지만 최신 버전의 솔리디티는 정수 수학의 오버플로/언더플로를 자동으로 확인합니다. + + +// 내장된 임의 함수 없음, 현재 블록 해시를 해시하여 의사 난수를 얻거나 체인링크 VRF와 같은 것을 사용하여 진정한 난수를 얻을 수 있습니다. +// https://docs.chain.link/docs/get-a-random-number + +// 유형 캐스팅 +int x = int(b); + +bool b = true; + +// 주소 - 20바이트/160비트 이더리움 주소 보유 +// 산술 허용 안 됨 +address public owner; + +// 계정 유형: +// 계약 계정: 생성 시 설정된 주소(생성자 주소의 함수, 전송된 트랜잭션 수) +// 외부 계정: (개인/외부 엔티티): 공개 키에서 생성된 주소 + +// 공개/외부에서 액세스할 수 있음을 나타내기 위해 'public' 필드 추가 +// getter는 자동으로 생성되지만 setter는 생성되지 않음 + +// 모든 주소로 이더를 보낼 수 있습니다. +owner.transfer(SOME_BALANCE); // 실패 시 실패하고 되돌립니다. + +// 더 낮은 수준의 .send 호출을 수행할 수도 있으며, 실패하면 false를 반환합니다. +if (owner.send(amount)) {} // 'if'로 send를 래핑하는 것을 기억하십시오. 계약 주소에는 +// send 시 실행되는 함수가 있으며 실패할 수 있습니다. +// 또한 재귀 호출 위험이 있으므로 send를 시도하기 전에 +// 잔액을 공제해야 합니다. 계약을 고갈시킬 수 있습니다. + +// 잔액 확인 가능 +owner.balance; // 소유자(사용자 또는 계약)의 잔액 + + +// 1에서 32까지 사용 가능한 바이트 +bytes1 a; // bytes1은 명시적 형식입니다. +bytes2 b; +bytes32 c; + +// 동적 크기 바이트 +bytes m; // 특수 배열, byte[] 배열과 동일(단, 꽉 채워짐) +// byte1-byte32보다 비싸므로 가능하면 그것들을 사용하십시오. + +// bytes와 동일하지만 길이 또는 인덱스 액세스를 허용하지 않음(현재로서는) +string n = "hello"; // UTF8로 저장됨, 작은따옴표가 아닌 큰따옴표 참고 +// 향후 추가될 문자열 유틸리티 함수 +// UTF8은 더 많은 저장 공간을 사용하므로 bytes32/bytes를 선호하십시오. + +// 기본적으로 모든 값은 인스턴스화 시 0으로 설정됩니다. + +// 대부분의 유형에서 Delete를 호출할 수 있습니다. +// (값을 파괴하지 않고 값을 0, 즉 초기 값으로 설정합니다) +delete x; + + +// 구조 분해/튜플 +(x, y) = (2, 7); // 여러 값 할당/교환 + + +// 2. 자료 구조 +// 배열 +bytes32[5] nicknames; // 정적 배열 +bytes32[] names; // 동적 배열 +names.push("John"); // 요소 추가(더 이상 길이를 반환하지 않음) +// 길이 +names.length; // 길이 가져오기 +// 참고: 직접 길이 할당은 최신 솔리디티 버전에서 제거되었습니다. + +// 다차원 배열 +uint[][5] x; // 동적 배열 요소 5개를 가진 배열(대부분의 언어와 반대 순서) + +// 사전(모든 유형에서 다른 유형으로) +mapping (string => uint) public balances; +balances["charles"] = 1; +// balances["ada"] 결과는 0, 설정되지 않은 모든 키 값은 0을 반환합니다. +// 'public'은 다른 계약에서 다음을 허용합니다. +contractName.balances("charles"); // 1 반환 +// 'public'은 다음과 같은 getter를 생성했습니다(setter는 아님). +function balances(string memory _account) public view returns (uint balance) { + return balances[_account]; +} + +// 중첩 매핑 +mapping (address => mapping (address => uint)) public custodians; + +// 삭제하려면 +delete balances["John"]; +delete balances; // 모든 요소를 0으로 설정 + +// 다른 언어와 달리 소스 키를 알지 못하면 +// 매핑의 모든 요소를 반복할 수 없습니다. +// 이를 위해 상위 데이터 구조를 빌드할 수 있습니다. + +// 구조체 +struct Bank { + address owner; + uint balance; +} +Bank b = Bank({ + owner: msg.sender, + balance: 5 +}); +// 또는 +Bank c = Bank(msg.sender, 5); + +c.balance = 5; // 새 값으로 설정 +delete b; +// 초기 값으로 설정, 매핑을 제외한 구조체의 모든 변수를 0으로 설정 + +// 열거형 +enum State { Created, Locked, Inactive }; // 상태 머신에 자주 사용됨 +State public state; // 열거형에서 변수 선언 +state = State.Created; +// 열거형은 명시적으로 int로 변환할 수 있습니다. +uint createdState = uint(State.Created); // 0 + +// 데이터 위치: 메모리 대 스토리지 대 콜데이터 - 모든 복합 유형(배열, +// 구조체)에는 데이터 위치가 있습니다. +// '메모리'는 지속되지 않고 '스토리지'는 지속됩니다. +// '콜데이터'도 지속되지 않으며 "읽기 전용"이므로 수정할 수 없습니다. +// 기본값은 로컬 및 상태 변수의 경우 '스토리지'이고 함수 매개변수의 경우 '메모리'입니다. +// 스택은 작은 로컬 변수를 보유합니다. + +// 대부분의 유형에 대해 사용할 데이터 위치를 명시적으로 설정할 수 있습니다. + + +// 3. 간단한 연산자 +// 비교, 비트 연산자 및 산술 연산자가 제공됩니다. +// 거듭제곱: ** +// 배타적 논리합: ^ +// 비트 부정: ~ + + +// 4. 주목할 만한 전역 변수 +// ** this ** +this; // 계약 주소 +// 종종 계약 수명 마지막에 남은 잔액을 당사자에게 이체하는 데 사용됩니다. +this.balance; +this.someFunction(); // 내부 점프가 아닌 호출을 통해 외부에서 함수 호출 + +// ** msg - 계약이 받은 현재 메시지 ** ** +msg.sender; // 보낸 사람 주소 +msg.value; // wei 단위로 이 계약에 제공된 이더 금액, 함수는 "payable"로 표시해야 합니다. +msg.data; // 바이트, 완전한 호출 데이터 + +// ** tx - 이 트랜잭션 ** +tx.origin; // 트랜잭션 보낸 사람 주소 +tx.gasprice; // 트랜잭션의 가스 가격 + +// ** block - 현재 블록에 대한 정보 ** +block.timestamp; // 현재 시간(대략)(유닉스 시간 사용) +// 이것은 채굴자에 의해 조작될 수 있으므로 주의해서 사용하십시오. + +block.number; // 현재 블록 번호 +block.difficulty; // 현재 블록 난이도 +block.blockhash(1); // bytes32 반환, 가장 최근 256개 블록에 대해서만 작동 +block.gasLimit(); + +// ** storage - 영구 저장소 해시 ** +storage['abc'] = 'def'; // 256비트 단어를 256비트 단어에 매핑 + + +// 5. 함수 등 +// A. 함수 +// 간단한 함수 +function increment(uint x) returns (uint) { + x += 1; + return x; +} + +// 함수는 많은 인수를 반환할 수 있으며, +// 반환된 인수 이름을 지정하면 명시적 반환이 필요하지 않습니다. +function increment(uint x, uint y) returns (uint x, uint y) { + x += 1; + y += 1; +} +// 이전 함수 호출 +(uint a, uint b) = increment(1,1); + +// 'view'(상수의 별칭) +// 함수가 영구 변수를 변경하지 않거나 변경할 수 없음을 나타냅니다. +// View 함수는 블록체인이 아닌 로컬에서 실행됩니다. +// 참고: 상수 키워드는 곧 사용되지 않을 예정입니다. +uint y = 1; + +function increment(uint x) view returns (uint x) { + x += 1; + y += 1; // 이 줄은 실패합니다. + // y는 상태 변수이며 view 함수에서 변경할 수 없습니다. +} + +// 'pure'는 'view' 또는 'constant'보다 더 엄격하며, +// 상태 변수 읽기도 허용하지 않습니다. +// 정확한 규칙은 더 복잡하므로 +// view/pure에 대해 자세히 알아보십시오: +// http://solidity.readthedocs.io/en/develop/contracts.html#view-functions + +// '함수 가시성 지정자' +// 이것들은 'view'가 있는 곳에 배치할 수 있으며 다음을 포함합니다. +// public - 외부 및 내부에서 볼 수 있음(함수의 기본값) +// external - 외부에서만 볼 수 있음(this.로 호출한 경우 포함) +// private - 현재 계약에서만 볼 수 있음 +// internal - 현재 계약 및 상속받는 계약에서만 볼 수 있음 + +// 일반적으로 각 함수를 명시적으로 표시하는 것이 좋습니다. + +// 함수 호이스팅 - 변수에 함수를 할당할 수 있습니다. +function a() { + function() internal z = b; + z(); +} + +function b() { + +} + +// 이더를 받는 모든 함수는 'payable'로 표시해야 합니다. +function depositEther() public payable { + balances[msg.sender] += msg.value; +} + + +// 재귀보다 루프를 선호하십시오(최대 호출 스택 깊이는 1024). +// 또한 경계가 없는 루프를 설정하지 마십시오. +// 가스 한도를 초과할 수 있습니다. + +// B. 이벤트 +// 이벤트는 외부 당사자에게 알립니다. 블록체인 외부에서 이벤트를 +// 검색하고 액세스하기 쉽습니다(경량 클라이언트 사용). +// 일반적으로 계약 매개변수 뒤에 선언합니다. + +// 일반적으로 대문자로 표시하고 명시성을 위해 앞에 Log를 추가하여 +// 함수 호출과 혼동을 방지합니다. + +// 선언 +event LogSent(address indexed from, address indexed to, uint amount); // 대문자 첫 글자 참고 + +// 호출 +emit LogSent(from, to, amount); + +/** + +외부 당사자(계약 또는 외부 엔티티)가 Web3 JavaScript 라이브러리를 사용하여 +감시하려면: + +// 다음은 솔리디티 코드가 아닌 JavaScript 코드입니다. +Coin.LogSent().watch({}, '', function(error, result) { + if (!error) { + console.log("Coin transfer: " + result.args.amount + + " coins were sent from " + result.args.from + + " to " + result.args.to + "."); + console.log("Balances now:\n" + + "Sender: " + Coin.balances.call(result.args.from) + + "Receiver: " + Coin.balances.call(result.args.to)); + } +} +**/ + +// 한 계약이 다른 계약에 의존하는 일반적인 패러다임(예: +// 다른 계약에서 제공하는 현재 환율에 의존하는 계약) + +// C. 수정자 +// 수정자는 최소 잔액 또는 사용자 인증과 같은 함수에 대한 +// 입력을 확인합니다. 다른 언어의 가드 절과 유사합니다. + +// '_' (밑줄)은 종종 본문 마지막 줄에 포함되며, +// 호출되는 함수가 거기에 배치되어야 함을 나타냅니다. +modifier onlyAfter(uint _time) { require (block.timestamp >= _time); _; } +modifier onlyOwner { require(msg.sender == owner); _; } +// 상태 머신과 함께 자주 사용됨 +modifier onlyIfStateA (State currState) { require(currState == State.A); _; } + +// 함수 선언 바로 뒤에 추가 +function changeOwner(newOwner) +onlyAfter(someTime) +onlyOwner() +onlyIfState(State.A) +{ + owner = newOwner; +} + +// 밑줄은 본문 끝 전에 포함될 수 있지만, +// 명시적으로 반환하면 건너뛰므로 주의해서 사용하십시오. +modifier checkValue(uint amount) { + _; + if (msg.value > amount) { + uint amountToRefund = amount - msg.value; + msg.sender.transfer(amountToRefund); + } +} + + +// 6. 분기 및 루프 + +// if/else, for, while, break, continue를 포함한 +// 모든 기본 논리 블록이 작동합니다. +// return - 하지만 switch는 없음 + +// 구문은 JavaScript와 동일하지만 부울이 아닌 +// 값에서 부울로의 유형 변환은 없습니다(부울 값을 얻으려면 +// 비교 연산자를 사용해야 함). + +// 사용자 행동에 의해 결정되는 For 루프의 경우, 계약에는 +// 코드 블록에 대한 최대 가스 양이 있으므로 주의하십시오. +// 초과하면 실패합니다. +// 예: +for(uint x = 0; x < refundAddressList.length; x++) { + refundAddressList[x].transfer(SOME_AMOUNT); +} + +// 위 두 가지 오류: +// 1. 전송 실패는 루프가 완료되는 것을 막고 돈을 묶습니다. +// 2. 이 루프는 임의로 길 수 있으므로(환불이 필요한 사용자 수에 따라), +// 블록의 최대 가스를 초과하므로 항상 실패할 수 있습니다. +// 대신, 사람들이 하위 계정에서 개별적으로 인출하고 인출된 것으로 표시하도록 해야 합니다. +// 예: 푸시 결제보다 풀 결제를 선호 + + +// 7. 객체/계약 + +// A. 외부 계약 호출 +contract InfoFeed { + function info() payable returns (uint ret) { return 42; } +} + +contract Consumer { + InfoFeed feed; // 블록체인의 계약을 가리킴 + + // 기존 계약 인스턴스에 피드 설정 + function setFeed(address addr) { + // 자동으로 캐스팅되므로 주의하십시오. 생성자는 호출되지 않습니다. + feed = InfoFeed(addr); + } + + // 계약의 새 인스턴스에 피드 설정 + function createNewFeed() { + feed = new InfoFeed(); // 새 인스턴스 생성, 생성자 호출 + } + + function callFeed() { + // 마지막 괄호는 계약을 호출하며, 선택적으로 + // 사용자 지정 이더 값 또는 가스를 추가할 수 있습니다. + feed.info{value: 10, gas: 800}(); + } +} + +// B. 상속 + +// 순서가 중요하며, 마지막으로 상속된 계약(즉, 'def')은 +// 이전에 상속된 계약의 일부를 재정의할 수 있습니다. +contract MyContract is abc, def("a custom argument to def") { + +// 함수 재정의 + function z() { + if (msg.sender == owner) { + def.z(); // def에서 재정의된 함수 호출 + super.z(); // 즉각적인 부모 재정의된 함수 호출 + } + } +} + +// 추상 함수 +function someAbstractFunction(uint x); +// 컴파일할 수 없으므로 구현되는 +// 기본/추상 계약에서 사용됩니다. + +// C. 가져오기 + +import "filename"; +import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol"; + + +// 8. 기타 키워드 + +// A. Selfdestruct +// 현재 계약을 자체 파괴하고 주소(종종 생성자)로 자금 보내기 +selfdestruct(SOME_ADDRESS); + +// 현재/미래 블록에서 스토리지/코드 제거 +// 씬 클라이언트에 도움이 되지만 이전 데이터는 블록체인에 지속됨 + +// 일반적인 패턴, 소유자가 계약을 종료하고 남은 자금을 받도록 함 +function remove() { + if(msg.sender == creator) { // 계약 생성자만 이 작업을 수행하도록 허용 + selfdestruct(creator); // 계약을 비활성화하고 자금 반환 + } +} + +// 자체 파괴 대신 수동으로 계약을 비활성화할 수 있음 +// (자체 파괴된 계약으로 전송된 이더는 손실됨) + + +// 9. 계약 디자인 노트 + +// A. 난독화 +// 모든 변수는 블록체인에서 공개적으로 볼 수 있으므로 +// 비공개인 모든 것은 난독화해야 합니다(예: 비밀로 해시). + +// 단계: 1. 무언가에 커밋, 2. 커밋 공개 +keccak256("some_bid_amount", "some secret"); // 커밋 + +// 나중에 계약의 공개 함수 호출 +// SHA3으로 해시되는 입찰가와 비밀 표시 +reveal(100, "mySecret"); + +// B. 스토리지 최적화 +// 데이터가 영원히 저장되므로 블록체인에 쓰는 것은 비용이 많이 들 수 있습니다. +// 메모리를 현명하게 사용하는 방법을 장려합니다(결국 컴파일이 더 나아지겠지만, +// 지금은 데이터 구조를 계획하고 블록체인에 최소한의 양을 저장하는 것이 +// 이점입니다). + +// 다차원 배열과 같은 항목은 비용이 많이 들 수 있습니다. +// (비용은 채워지지 않은 변수를 선언하는 것이 아니라 데이터를 저장하는 데 따름) + +// C. 블록체인의 데이터 액세스 +// 인간이나 컴퓨터가 트랜잭션 또는 트랜잭션 상태의 +// 내용을 읽는 것을 제한할 수 없습니다. + +// 'private'은 다른 *계약*이 데이터를 직접 읽는 것을 +// 방지하지만 다른 모든 당사자는 여전히 블록체인의 데이터를 읽을 수 있습니다. + +// 시간 시작까지의 모든 데이터는 블록체인에 저장되므로 +// 누구나 모든 이전 데이터와 변경 사항을 관찰할 수 있습니다. + +// D. 오라클 및 외부 데이터 +// 오라클은 블록체인 외부에서 스마트 계약과 상호 작용하는 방법입니다. +// 실제 세계에서 데이터를 가져오거나, 실제 세계에 post 요청을 보내거나 +// 그 반대로 하는 데 사용됩니다. + +// 계약은 직접 호출해야 하고 시간에 "구독"할 수 없으므로 +// 시간 기반 계약 구현도 오라클을 통해 수행됩니다. +// 스마트 계약이 분산되어 있기 때문에 데이터를 +// 분산된 방식으로 가져오고 싶을 것입니다. 그렇지 않으면 +// 스마트 계약 설계 문제가 방지하는 중앙 집중식 위험에 직면하게 됩니다. + +// 미리 상자에 담긴 분산 데이터를 얻고 사용하는 가장 쉬운 방법은 체인링크 데이터 피드입니다. +// https://docs.chain.link/docs/get-the-latest-price +// 여러 소스에서 이미 집계되어 온체인으로 전달된 온체인 참조 지점을 +// 참조할 수 있으며, 이를 "데이터 뱅크"로 사용할 수 있습니다. +// 소스의. + +// 여기에서 API를 호출하는 다른 예를 볼 수 있습니다: +// https://docs.chain.link/docs/make-a-http-get-request + +// 그리고 물론 자신만의 오라클 네트워크를 구축할 수 있습니다. +// 애플리케이션이 얼마나 중앙 집중식인지 분산되어 있는지 확인하십시오. + +// 직접 오라클 네트워크 설정 + +// E. 크론 작업 +// 계약은 시간 기반 스케줄링을 처리하기 위해 수동으로 호출해야 합니다. +// 정기적으로 핑을 보내거나 다른 사람에게 인센티브(이더)를 제공하는 +// 외부 코드를 만들 수 있습니다. + +// F. 옵저버 패턴 +// 옵저버 패턴을 사용하면 구독자로 등록하고 +// 오라클에서 호출하는 함수를 등록할 수 있습니다(참고, 오라클은 +// 이 작업 실행 비용을 지불합니다). +// Pub/sub의 구독과 일부 유사점 + +// 이것은 추상 계약이며, 클라이언트와 서버 클래스 모두 +// 클라이언트가 구현해야 하는 것을 가져옵니다. +contract SomeOracleCallback { + function oracleCallback(int _value, uint _time, bytes32 info) external; +} + +contract SomeOracle { + SomeOracleCallback[] callbacks; // 모든 구독자 배열 + + // 구독자 등록 + function addSubscriber(SomeOracleCallback a) { + callbacks.push(a); + } + + function notify(value, time, info) private { + for(uint i = 0;i < callbacks.length; i++) { + // 호출된 모든 구독자는 oracleCallback을 구현해야 합니다. + callbacks[i].oracleCallback(value, time, info); + } + } + + function doSomething() public { + // 무언가를 하는 코드 + + // 모든 구독자에게 알림 + notify(_value, _time, _info); + } +} + +// 이제 클라이언트 계약은 SomeOracleCallback을 가져와 +// Some Oracle에 등록하여 addSubscriber를 추가할 수 있습니다. + +// G. 상태 머신 +// State 열거형 및 inState 수정자에 대한 아래 예 참조 +``` + +아래 전체 예제를 [`Remix의 Remix VM`을 사용하여 여기서 작업하십시오.](https://remix.ethereum.org/#version=soljson-v0.8.19+commit7dd6d404.js&optimize=false&evmVersion=null&gist=2acb47a0286fe45d2464fa937f00fef3&runs=200) + +```solidity +// *** 예제: 크라우드펀딩 예제(킥스타터와 대체로 유사) *** +// ** 예제 시작 ** + +// CrowdFunder.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +/// @title CrowdFunder +/// @author nemild +contract CrowdFunder { + // 생성자가 생성 시 설정하는 변수 + address public creator; + address payable public fundRecipient; // 생성자는 수신자와 다를 수 있으며 payable이어야 함 + uint public minimumToRaise; // 팁을 주기 위해 필요, 그렇지 않으면 모두 환불받음 + string campaignUrl; + uint256 version = 1; + + // 자료 구조 + enum State { + Fundraising, + ExpiredRefund, + Successful + } + struct Contribution { + uint amount; + address payable contributor; + } + + // 상태 변수 + State public state = State.Fundraising; // 생성 시 초기화 + uint public totalRaised; + uint public raiseBy; + uint public completeAt; + Contribution[] contributions; + + event LogFundingReceived(address addr, uint amount, uint currentTotal); + event LogWinnerPaid(address winnerAddress); + + modifier inState(State _state) { + require(state == _state); + _; + } + + modifier isCreator() { + require(msg.sender == creator); + _; + } + + // 계약 파기를 허용하기 전에 최종 계약 상태 후 24주 대기 + modifier atEndOfLifecycle() { + require(((state == State.ExpiredRefund || state == State.Successful) && + completeAt + 24 weeks < block.timestamp)); + _; + } + + function crowdFund( + uint timeInHoursForFundraising, + string memory _campaignUrl, + address payable _fundRecipient, + uint _minimumToRaise) + public + { + creator = msg.sender; + fundRecipient = _fundRecipient; + campaignUrl = _campaignUrl; + minimumToRaise = _minimumToRaise; + raiseBy = block.timestamp + (timeInHoursForFundraising * 1 hours); + } + + function contribute() + public + payable + inState(State.Fundraising) + returns(uint256 id) + { + contributions.push( + Contribution({ + amount: msg.value, + contributor: payable(msg.sender) + }) // 배열 사용, 반복 가능 + ); + totalRaised += msg.value; + + emit LogFundingReceived(msg.sender, msg.value, totalRaised); + + checkIfFundingCompleteOrExpired(); + return contributions.length - 1; // id 반환 + } + + function checkIfFundingCompleteOrExpired() + public + { + if (totalRaised > minimumToRaise) { + state = State.Successful; + payOut(); + + // 상태 변경을 시작한 발신자에게 인센티브를 줄 수 있음 + } else if ( block.timestamp > raiseBy ) { + state = State.ExpiredRefund; // 후원자는 이제 getRefund(id)를 호출하여 환불을 받을 수 있음 + } + completeAt = block.timestamp; + } + + function payOut() + public + inState(State.Successful) + { + fundRecipient.transfer(address(this).balance); + emit LogWinnerPaid(fundRecipient); + } + + function getRefund(uint256 id) + inState(State.ExpiredRefund) + public + returns(bool) + { + require(contributions.length > id && id >= 0 && contributions[id].amount != 0 ); + + uint256 amountToRefund = contributions[id].amount; + contributions[id].amount = 0; + + contributions[id].contributor.transfer(amountToRefund); + + return true; + } + + // 경고: "selfdestruct"는 더 이상 사용되지 않습니다. + // 칸쿤 하드 포크부터 기본 opcode는 더 이상 + // 계정과 관련된 코드 및 데이터를 삭제하지 않고 이더만 수혜자에게 전송합니다. + // 계약이 생성된 동일한 트랜잭션에서 실행되지 않는 한(EIP-6780 참조). + + // 새로운 동작을 고려하더라도 새로 배포된 계약에서 사용하는 것은 + // 강력히 권장되지 않습니다. + // EVM에 대한 향후 변경 사항은 opcode의 기능을 더욱 감소시킬 수 있습니다. + // function removeContract() + // public + // isCreator() + // atEndOfLifecycle() + // { + // selfdestruct(msg.sender); + // // 생성자는 청구되지 않은 모든 돈을 받습니다. + // } +} +// ** 예제 끝 ** +``` + +몇 가지 추가 기능. + +```solidity +// 10. 기타 네이티브 함수 + +// 통화 단위 +// 통화는 가장 작은 이더 단위인 wei를 사용하여 정의됩니다. +uint minAmount = 1 wei; +uint a = 1 finney; // 1 ether == 1000 finney +// 다른 단위, 참조: http://ether.fund/tool/converter + +// 시간 단위 +1 == 1 second +1 minutes == 60 seconds + +// 단위는 변수에 저장되지 않으므로 변수에 단위를 곱할 수 있습니다. +uint x = 5; +(x * 1 days); // 5 days + +// 시간에 대한 등호 문으로 윤초/년에 주의하십시오. +// (대신, 보다 큼/작음 선호) + +// 암호화 +// 전달된 모든 문자열은 해시 작업 전에 연결됩니다. +keccak256("ab", "cd"); +ripemd160("abc"); +sha256("def"); + +// 11. 보안 + +// 이더리움 계약에서 버그는 재앙이 될 수 있으며, 솔리디티의 인기 있는 패턴조차도 +// 안티패턴으로 발견될 수 있습니다. + +// 이 문서 끝에 있는 보안 링크 참조 + +// 12. 저수준 함수 +// call - 저수준, 자주 사용되지 않음, 유형 안전성을 제공하지 않음 +(bool success, bytes memory data) = someContractAddress.call( + abi.encodeWithSignature("function_name(string,string)", "arg1", "arg2") +); + +// delegatecall - 대상 주소의 코드가 호출 계약의 *컨텍스트*에서 실행됨 +// 라이브러리 기능 제공 +(bool success, bytes memory data) = someContractAddress.delegatecall( + abi.encodeWithSignature("function_name()") +); + + +// 13. 스타일 노트 +// Python의 PEP8 스타일 가이드 기반 +// 전체 스타일 가이드: http://solidity.readthedocs.io/en/develop/style-guide.html + +// 간단한 요약: +// 들여쓰기 4칸 +// 계약 선언(및 기타 최상위 선언)을 구분하는 두 줄 +// 괄호 안의 불필요한 공백 피하기 +// 한 줄 문(if, for 등)에 대해 중괄호 생략 가능 +// else는 자체 줄에 배치해야 함 + + +// 14. NATSPEC 주석 +// 문서화, 주석 및 외부 UI에 사용됨 + +// 계약 natspec - 항상 계약 정의 위에 +/// @title 계약 제목 +/// @author 저자 이름 + +// 함수 natspec +/// @notice 함수가 하는 일에 대한 정보, 실행할 함수일 때 표시됨 +/// @dev 개발자를 위한 함수 문서 + +// 함수 매개변수/반환 값 natspec +/// @param someParam 매개변수가 하는 일에 대한 설명 +/// @return 반환 값에 대한 설명 +``` + +## 추가 자료 +- [솔리디티 문서](https://solidity.readthedocs.org/en/latest/) +- [Cyfrin Updraft (솔리디티 개발 배우기)](https://updraft.cyfrin.io/) +- [체인링크 초보자 튜토리얼](https://docs.chain.link/docs/beginners-tutorial) +- [스마트 계약 모범 사례](https://github.com/ConsenSys/smart-contract-best-practices) +- [브라우저 기반 솔리디티 편집기](https://remix.ethereum.org/) +- [이더리움 계약을 위한 모듈식 디자인 전략](https://docs.erisindustries.com/tutorials/solidity/) +- [체인링크 문서](https://docs.chain.link/docs/getting-started) + +## 스마트 계약 개발 프레임워크 +- [Foundry](https://getfoundry.sh/) +- [Hardhat](https://hardhat.org/) +- [Brownie](https://github.com/eth-brownie/brownie) + +## 중요한 라이브러리 +- [OpenZeppelin](https://github.com/OpenZeppelin/openzeppelin-contracts): 일반적인 계약 패턴(크라우드펀딩, safemath 등)을 제공하는 라이브러리 +- [체인링크](https://github.com/smartcontractkit/chainlink): 외부 데이터와 상호 작용할 수 있는 코드 + +## 샘플 계약 +- [Dapp Bin](https://github.com/ethereum/dapp-bin) +- [Defi 예제](https://github.com/PatrickAlphaC/chainlink_defi) +- [솔리디티 아기 걸음 계약](https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts) +- [ConsenSys 계약](https://github.com/ConsenSys/dapp-store-contracts) +- [Dapps의 상태](http://dapps.ethercasts.com/) +- [보안 코드베이스 예제](https://github.com/Cyfrin/sc-exploits-minimized/) + +## 보안 +- [스마트 계약 보안 커리큘럼](https://updraft.cyfrin.io/courses/security) +- [스마트 계약 보안 보고서 데이터베이스](https://solodit.xyz/) +- [스마트 계약 보안에 대한 생각](https://blog.ethereum.org/2016/06/19/thinking-smart-contract-security/) +- [스마트 계약 보안](https://blog.ethereum.org/2016/06/10/smart-contract-security/) +- [Hacking Distributed 블로그](http://hackingdistributed.com/) + +## 스타일 +- [솔리디티 스타일 가이드](http://solidity.readthedocs.io/en/latest/style-guide.html): 이더리움의 스타일 가이드는 Python의 [PEP 8](https://www.python.org/dev/peps/pep-0008/) 스타일 가이드에서 크게 파생되었습니다. + +## 편집기 +- [Remix](https://remix.ethereum.org/) +- [Emacs 솔리디티 모드](https://github.com/ethereum/emacs-solidity) +- [Vim 솔리디티](https://github.com/tomlion/vim-solidity) +- 편집기 스니펫 ([Ultisnips 형식](https://gist.github.com/nemild/98343ce6b16b747788bc)) + +## 향후 할 일 +- 일반적인 디자인 패턴 목록(조절, RNG, 버전 업그레이드) +- 일반적인 보안 안티 패턴 + +편집 내용이 있는 풀 리퀘스트를 보내거나 nemild -/at-/ gmail로 이메일을 보내주십시오. diff --git a/ko/sorbet.md b/ko/sorbet.md new file mode 100644 index 0000000000..2bec886e8f --- /dev/null +++ b/ko/sorbet.md @@ -0,0 +1,1044 @@ +--- +name: Sorbet +filename: learnsorbet.rb +contributors: + - ["Jeremy Kaplan", "https://jdkaplan.dev"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Sorbet은 Ruby용 타입 체커입니다. 정적 및 런타임 타입 검사를 모두 활성화하는 메서드 서명 구문을 추가합니다. + +가장 쉽게 작동하는 것을 볼 수 있는 방법은 [sorbet.run](https://sorbet.run)의 플레이그라운드입니다. + +아래 섹션 중 하나를 복사해 보세요! 각 최상위 `class` 또는 `module`은 다른 것과 독립적입니다. + +```ruby +# 모든 파일에는 정적 타입 검사 중에 Sorbet이 얼마나 엄격해야 하는지를 알려주는 +# "타입 시길"이 있어야 합니다. +# +# 엄격도 수준 (느슨함에서 엄격함 순): +# +# ignore: Sorbet이 파일을 읽지 않습니다. 즉, 타입 검사 중에 +# 해당 내용이 보이지 않습니다. 이것은 피하십시오. +# +# false: Sorbet은 상수 확인과 관련된 오류만 보고합니다. +# 시길이 포함되지 않은 경우 기본값입니다. +# +# true: Sorbet은 모든 정적 타입 오류를 보고합니다. 이것은 +# 노력 대비 안전성의 최적점입니다. +# +# strict: Sorbet은 모든 메서드, 상수 및 인스턴스 +# 변수에 정적 타입이 있어야 합니다. +# +# strong: Sorbet은 더 이상 명시적으로도 T.untyped를 허용하지 않습니다. +# 거의 아무것도 이것을 만족시키지 못합니다. + +# typed: true + +# 런타임 타입 검사 라이브러리를 포함합니다. 이렇게 하면 인라인 시그를 작성하고 +# 런타임에 검사할 수 있습니다(RBI 전용으로 Sorbet을 실행하는 대신). +# 이러한 런타임 검사는 `ignore` 또는 `false` 시길이 있는 파일에서도 발생합니다. +require 'sorbet-runtime' + +class BasicSigs + # 타입 정의 도우미를 가져옵니다. 거의 항상 필요합니다. + extend T::Sig + + # 시그는 `sig`와 블록으로 정의됩니다. `returns`로 반환 값 타입을 + # 정의합니다. + # + # 이 메서드는 클래스가 `String`인 값을 반환합니다. 이것들은 가장 + # 일반적인 타입이며 Sorbet은 이를 "클래스 타입"이라고 부릅니다. + sig { returns(String) } + def greet + 'Hello, World!' + end + + # `params`로 매개변수 값 타입을 정의합니다. + sig { params(n: Integer).returns(String) } + def greet_repeat(n) + (1..n).map { greet }.join("\n") + end + + # 키워드 매개변수도 같은 방식으로 정의합니다. + sig { params(n: Integer, sep: String).returns(String) } + def greet_repeat_2(n, sep: "\n") + (1..n).map { greet }.join(sep) + end + + # 위치/키워드 및 필수/선택 사항은 여기서 차이가 없습니다. + # 모두 `params`에서 같은 방식으로 정의됩니다. + + # 매개변수가 많은 경우 중괄호 대신 do..end와 여러 줄 블록을 + # 사용하는 것이 더 좋습니다. + sig do + params( + str: String, + num: Integer, + sym: Symbol, + ).returns(String) + end + def uhh(str:, num:, sym:) + 'What would you even do with these?' + end + + # 반환 값이 쓸모없는 메서드의 경우 `void`를 사용합니다. + sig { params(name: String).void } + def say_hello(name) + puts "Hello, #{name}!" + end + + # 스플랫! "나머지 매개변수", "*args", "**kwargs" 등으로도 알려져 있습니다. + # + # `args` 또는 `kwargs` 자체의 값이 아닌 `args` 또는 `kwargs`의 + # _멤버_가 가질 값의 타입을 지정합니다. + sig { params(args: Integer, kwargs: String).void } + def no_op(*args, **kwargs) + if kwargs[:op] == 'minus' + args.each { |i| puts(i - 1) } + else + args.each { |i| puts(i + 1) } + end + end + + # 대부분의 초기화자는 `void`여야 합니다. + sig { params(name: String).void } + def initialize(name:) + # 인스턴스 변수는 정적 타입 검사에 참여하려면 + # 주석이 달린 타입이 있어야 합니다. + + # `T.let`의 값은 정적으로 그리고 런타임에 확인됩니다. + @upname = T.let(name.upcase, String) + + # Sorbet은 이것을 추론할 수 있습니다! + @name = name + end + + # 상수에도 주석이 달린 타입이 필요합니다. + SORBET = T.let('A delicious frozen treat', String) + + # 클래스 변수도 마찬가지입니다. + @@the_answer = T.let(42, Integer) + + # Sorbet은 `attr_*` 계열을 알고 있습니다. + sig { returns(String) } + attr_reader :upname + + sig { params(write_only: Integer).returns(Integer) } + attr_writer :write_only + + # 리더 부분을 말하면 Sorbet이 라이터 부분을 말할 것입니다. + sig { returns(String) } + attr_accessor :name +end + +module Debugging + extend T::Sig + + # 때로는 Sorbet이 표현식에 대해 어떤 타입을 추론했는지 아는 것이 + # 도움이 될 때가 있습니다. `T.reveal_type`을 사용하여 타입 검사에서 + # 해당 정보와 함께 특별한 오류를 표시하도록 합니다. + # + # 이것은 Sorbet을 편집기에 통합하여 파일을 저장하자마자 + # 결과를 볼 수 있는 경우에 가장 유용합니다. + + sig { params(obj: Object).returns(String) } + def debug(obj) + T.reveal_type(obj) # 공개된 타입: Object + repr = obj.inspect + + # Ruby 메서드는 인수 없이 호출할 수 있으므로 몇 글자를 + # 절약할 수 있습니다! + T.reveal_type repr # 공개된 타입: String + + "DEBUG: " + repr + end +end + +module StandardLibrary + extend T::Sig + # Sorbet은 Ruby 표준 라이브러리를 타이핑하는 데 도움이 되는 몇 가지 + # 도우미를 제공합니다. + + # `true`와 `false`를 모두 잡으려면 T::Boolean을 사용하십시오. + # + # 궁금한 분들을 위해, 이것은 다음과 같습니다. + # + # T.type_alias { T.any(TrueClass, FalseClass) } + # + sig { params(str: String).returns(T::Boolean) } + def confirmed?(str) + str == 'yes' + end + + # 값 `nil`은 NilClass의 인스턴스임을 기억하십시오. + sig { params(val: NilClass).void } + def only_nil(val:); end + + # 표준 라이브러리 클래스를 수정하지 않기 위해 Sorbet은 일반적인 + # 제네릭을 지원하는 래퍼를 제공합니다. + # + # 전체 목록은 다음과 같습니다: + # * T::Array + # * T::Enumerable + # * T::Enumerator + # * T::Hash + # * T::Range + # * T::Set + sig { params(config: T::Hash[Symbol, String]).returns(T::Array[String]) } + def merge_values(config) + keyset = [:old_key, :new_key] + config.each_pair.flat_map do |key, value| + keyset.include?(key) ? value : 'sensible default' + end + end + + # 때로는 (보통 의존성 주입) 메서드가 클래스의 인스턴스가 아닌 + # 클래스에 대한 참조를 허용합니다. `Dep` 클래스 자체(또는 + # 상속받는 것)를 허용하려면 `T.class_of(Dep)`를 사용하십시오. + class Dep; end + + sig { params(dep: T.class_of(Dep)).returns(Dep) } + def dependency_injection(dep:) + dep.new + end + + # 블록, proc 및 람다, 오 세상에! 이 모든 것은 `T.proc`으로 타입이 지정됩니다. + # + # 제한 사항: + # 1. 모든 매개변수는 필수 위치 매개변수로 가정됩니다. + # 2. 유일한 런타임 검사는 값이 `Proc`이라는 것입니다. 인수 타입은 + # 정적으로만 확인됩니다. + sig do + params( + data: T::Array[String], + blk: T.proc.params(val: String).returns(Integer), + ).returns(Integer) + end + def count(data, &blk) + data.sum(&blk) + end + + sig { returns(Integer) } + def count_usage + count(["one", "two", "three"]) { |word| word.length + 1 } + end + + # 메서드에 암시적 블록이 있는 경우 Sorbet은 `T.untyped`를 추론합니다. + # 타입이 중요한 경우 명시적 블록 구문을 사용하십시오. + sig { params(str: String).returns(T.untyped) } + def implicit_block(str) + yield(str) + end + + # DSL을 작성하고 다른 컨텍스트에서 블록을 실행하는 경우 + # `bind`를 사용하십시오. + sig { params(num: Integer, blk: T.proc.bind(Integer).void).void } + def number_fun(num, &blk) + num.instance_eval(&blk) + end + + sig { params(num: Integer).void } + def number_fun_usage(num) + number_fun(10) { puts digits.join } + end + + # 블록에 매개변수가 없는 경우 `params`를 포함하지 마십시오. + sig { params(blk: T.proc.returns(Integer)).returns(Integer) } + def doubled_block(&blk) + 2 * blk.call + end +end + +module Combinators + extend T::Sig + # 이러한 메서드를 사용하면 기존 타입에서 새 타입을 정의할 수 있습니다. + + # 여러 타입 중 하나일 수 있는 값이 있는 경우 `T.any`를 사용하십시오. + # 이것들은 때때로 "유니온 타입" 또는 "합 타입"으로 알려져 있습니다. + sig { params(num: T.any(Integer, Float)).returns(Rational) } + def hundreds(num) + num.rationalize + end + + # `T.nilable(Type)`은 `T.any(Type, NilClass)`의 편리한 별칭입니다. + sig { params(val: T.nilable(String)).returns(Integer) } + def strlen(val) + val.nil? ? -1 : val.length + end + + # 여러 타입을 만족해야 하는 값이 있는 경우 `T.all`을 사용하십시오. + # 이것들은 때때로 "교차 타입"으로 알려져 있습니다. 인터페이스(나중에 + # 설명)에 가장 유용하지만 도우미 모듈을 설명하는 데도 사용할 수 있습니다. + + module Reversible + extend T::Sig + sig { void } + def reverse + # 실제로 구현된 척 + end + end + + module Sortable + extend T::Sig + sig { void } + def sort + # 실제로 구현된 척 + end + end + + class List + include Reversible + include Sortable + end + + sig { params(list: T.all(Reversible, Sortable)).void } + def rev_sort(list) + # Reversible의 reverse + list.reverse + # Sortable의 sort + list.sort + end + + def rev_sort_usage + rev_sort(List.new) + end + + # 때로는 실제로 모든 시간을 타입을 명시하는 것이 도움이 되기보다 + # 더 혼란스러울 때가 있습니다. 타입 별칭을 사용하여 더 쉽게 + # 작업할 수 있습니다. + JSONLiteral = T.type_alias { T.any(Float, String, T::Boolean, NilClass) } + + sig { params(val: JSONLiteral).returns(String) } + def stringify(val) + val.to_s + end +end + +module DataClasses + extend T::Sig + # `T::Struct`를 사용하여 타입 검사 필드가 있는 새 클래스를 만듭니다. + # 표준 Struct와 OpenStruct의 가장 좋은 부분을 결합한 다음 + # 정적 타이핑을 추가합니다. + # + # 이 방법으로 구성된 타입은 때때로 "곱 타입"으로 알려져 있습니다. + + class Matcher < T::Struct + # `prop`을 사용하여 리더와 라이터가 모두 있는 필드를 정의합니다. + prop :count, Integer + # `const`를 사용하여 리더만 정의하고 라이터는 건너뜁니다. + const :pattern, Regexp + # `default`로 기본값을 설정할 수 있습니다. + const :message, String, default: 'Found one!' + + # 이것은 그렇지 않으면 일반 클래스이므로 여전히 메서드를 정의할 수 있습니다. + + # 사용하려면 여전히 `sig`를 가져와야 합니다. + extend T::Sig + + sig { void } + def reset + self.count = 0 + end + end + + sig { params(text: String, matchers: T::Array[Matcher]).void } + def awk(text, matchers) + matchers.each(&:reset) + text.lines.each do |line| + matchers.each do |matcher| + if matcher.pattern =~ line + Kernel.puts matcher.message + matcher.count += 1 + end + end + end + end + + # 함정과 제한 사항 + + # 1. `const` 필드는 진정으로 불변이 아닙니다. 라이터 메서드는 없지만 + # 다른 방식으로 변경될 수 있습니다. + class ChangeMe < T::Struct + const :list, T::Array[Integer] + end + + sig { params(change_me: ChangeMe).returns(T::Boolean) } + def whoops!(change_me) + change_me = ChangeMe.new(list: [1, 2, 3, 4]) + change_me.list.reverse! + change_me.list == [4, 3, 2, 1] + end + + # 2. `T::Struct`는 `BasicObject`에서 동등성 메서드를 상속하며, + # 이는 항등 동등성(참조 동등성이라고도 함)을 사용합니다. + class Coordinate < T::Struct + const :row, Integer + const :col, Integer + end + + sig { returns(T::Boolean) } + def never_equal! + p1 = Coordinate.new(row: 1, col: 2) + p2 = Coordinate.new(row: 1, col: 2) + p1 != p2 + end + + # 원하는 경우 필드를 확인하는 자신만의 `#==` 메서드를 정의하십시오. + class Position < T::Struct + extend T::Sig + + const :x, Integer + const :y, Integer + + sig { params(other: Object).returns(T::Boolean) } + def ==(other) + # 실제 구현은 여기에 있습니다: + # https://github.com/tricycle/sorbet-struct-comparable + true + end + end + + # `T::Enum`을 사용하여 쉽게 참조할 수 있는 고정된 값 집합을 정의합니다. + # 이것은 값 자체가 무엇인지보다 가능성 집합이 닫혀 있고 + # 정적이라는 점에 더 신경 쓰는 경우에 특히 유용합니다. + class Crayon < T::Enum + extend T::Sig + + # `enums`로 멤버를 초기화합니다. + enums do + # 각 멤버를 `new`로 정의합니다. 이들 각각은 + # `Crayon` 클래스의 인스턴스입니다. + Red = new + Orange = new + Yellow = new + Green = new + Blue = new + Violet = new + Brown = new + Black = new + # 열거형의 기본값은 모두 소문자로 된 이름입니다. + # 변경하려면 `new`에 값을 전달하십시오. + Gray90 = new('light-gray') + end + + sig { returns(String) } + def to_hex + case self + when Red then '#ff0000' + when Green then '#00ff00' + # ... + else '#ffffff' + end + end + end + + sig { params(crayon: Crayon, path: T::Array[Position]).void } + def draw(crayon:, path:) + path.each do |pos| + Kernel.puts "(#{pos.x}, #{pos.y}) = " + crayon.to_hex + end + end + + # 열거형의 모든 값을 얻으려면 `.values`를 사용하십시오. 편의를 위해 + # 열거형 문자열 값을 얻는 `#serialize`가 이미 있습니다. + + sig { returns(T::Array[String]) } + def crayon_names + Crayon.values.map(&:serialize) + end + + # 문자열에서 열거형 값으로 이동하려면 "deserialize" 계열을 사용하십시오. + + sig { params(name: String).returns(T.nilable(Crayon)) } + def crayon_from_name(name) + if Crayon.has_serialized?(name) + # 값이 없는 경우 `KeyError`가 발생합니다. + Crayon.deserialize(name) + end + + # 값이 없는 경우 `nil`을 반환합니다. + Crayon.try_deserialize(name) + end +end + +module FlowSensitivity + extend T::Sig + # Sorbet은 Ruby의 제어 흐름 구문을 이해하고 해당 정보를 사용하여 + # 코드가 분기될 때 더 정확한 타입을 얻습니다. + + # nil 검사를 할 때 가장 자주 보게 될 것입니다. + sig { params(name: T.nilable(String)).returns(String) } + def greet_loudly(name) + if name.nil? + 'HELLO, YOU!' + else + # Sorbet은 `name`이 여기서 String이어야 함을 알고 있으므로 `#upcase`를 + # 호출하는 것이 안전합니다. + "HELLO, #{name.upcase}!" + end + end + + # nil은 `T.any`를 구체화하는 특별한 경우입니다. + sig { params(id: T.any(Integer, T::Array[Integer])).returns(T::Array[String]) } + def database_lookup(id) + if id.is_a?(Integer) + # 여기서 `ids`는 Integer여야 합니다. + [id.to_s] + else + # 여기서 `ids`는 T::Array[Integer]여야 합니다. + id.map(&:to_s) + end + end + + # Sorbet은 타입 정의를 좁히는 다음 메서드를 인식합니다: + # * is_a? + # * kind_of? + # * nil? + # * Class#=== + # * Class#< + # * block_given? + # + # 매우 일반적이므로 다음 Rails 확장도 인식합니다: + # * blank? + # * present? + # + # 이러한 메서드를 재정의하는 경우 Sorbet 가정을 유지하도록 주의하십시오! + + # 이 코드 줄을 작성한 적이 있습니까? + # + # raise StandardError, "Can't happen" + # + # Sorbet은 `T.absurd`를 사용하여 정적으로 이를 증명하는 데 도움이 될 수 있습니다 + # (이것은 "철저함"으로 알려져 있습니다). `T::Enum`과 결합하면 + # 더욱 멋집니다! + + class Size < T::Enum + extend T::Sig + + enums do + Byte = new('B') + Kibibyte = new('KiB') + Mebibyte = new('MiB') + # "640K ought to be enough for anybody" + end + + sig { returns(Integer) } + def bytes + case self + when Byte then 1 << 0 + when Kibibyte then 1 << 10 + when Mebibyte then 1 << 20 + else + # Sorbet은 모든 경우를 확인했음을 알고 있으므로 `self`가 + # 여기서 가질 수 있는 가능한 값은 없습니다. + # + # 하지만 어떻게든 여기에 도달하면 런타임에 오류가 발생합니다. + T.absurd(self) + + # 누락된 케이스가 있는 경우 Sorbet은 어떤 것인지 알려줄 수도 있습니다! + end + end + end + + # 다음 부분에는 `puts`와 `raise`가 필요합니다. + include Kernel + + # Sorbet은 `raise` 문 뒤에는 코드를 실행할 수 없다는 것을 알고 있습니다. + # 왜냐하면 "절대 반환하지 않기" 때문입니다. + sig { params(num: T.nilable(Integer)).returns(Integer) } + def decrement(num) + raise ArgumentError, '¯\_(ツ)_/¯' unless num + + num - 1 + end + + class CustomError < StandardError; end + + # `T.noreturn`으로 자신만의 오류 발생 메서드에 주석을 달 수 있습니다. + sig { params(message: String).returns(T.noreturn) } + def oh_no(message = 'A bad thing happened') + puts message + raise CustomError, message + end + + # 무한 루프도 반환하지 않습니다. + sig { returns(T.noreturn) } + def loading + loop do + %q(-\|/).each_char do |c| + print "\r#{c} reticulating splines..." + sleep 1 + end + end + end + + # Sorbet이 타입 구체화를 "잃어버리는" 상황에 직면할 수 있습니다. + # Ruby에서 하는 거의 모든 것이 다음 번에 호출할 때 다른 값을 + # 반환할 수 있는 메서드 호출이라는 것을 기억하십시오. Sorbet은 + # (`attr_reader` 및 `attr_accessor`의 메서드조차도) 어떤 메서드도 + # 순수하다고 가정하지 않습니다. + sig { returns(T.nilable(Integer)) } + def answer + rand > 0.5 ? 42 : nil + end + + sig { void } + def bad_typecheck + if answer.nil? + 0 + else + # 하지만 다시 호출하면 answer가 `nil`을 반환할 수 있습니다! + answer + 1 + # ^ 메서드 +가 T.nilable(Integer)의 NilClass 구성 요소에 존재하지 않음 + end + end + + sig { void } + def good_typecheck + ans = answer + if ans.nil? + 0 + else + # 이번에는 Sorbet이 `ans`가 nil이 아님을 알고 있습니다. + ans + 1 + end + end +end + +module InheritancePatterns + extend T::Sig + + # 항상 수신자의 타입을 반환하는 메서드가 있는 경우 + # `T.self_type`을 사용하십시오. 이것은 유창한 인터페이스와 DSL에서 + # 일반적입니다. + # + # 경고: 이 기능은 아직 실험적입니다! + class Logging + extend T::Sig + + sig { returns(T.self_type) } + def log + pp self + self + end + end + + class Data < Logging + extend T::Sig + + sig { params(x: Integer, y: String).void } + def initialize(x: 0, y: '') + @x = x + @y = y + end + + # 관련 클래스가 하나뿐인 경우 `T.self_type`을 사용할 필요는 없습니다. + sig { params(x: Integer).returns(Data) } + def setX(x) + @x = x + self + end + + sig { params(y: String).returns(Data) } + def setY(y) + @y = y + self + end + end + + # 짜잔! + sig { params(data: Data).void } + def chaining(data) + data.setX(1).log.setY('a') + end + + # 클래스 메서드(싱글톤 메서드라고도 함)인 경우 `T.attached_class`를 사용하십시오. + # + # 여기에는 경고가 없습니다. 이것은 안정적입니다! + class Box + extend T::Sig + + sig { params(contents: String, weight: Integer).void } + def initialize(contents, weight) + @contents = contents + @weight = weight + end + + sig { params(contents: String).returns(T.attached_class) } + def self.pack(contents) + new(contents, contents.chars.uniq.length) + end + end + + class CompanionCube < Box + extend T::Sig + + sig { returns(String) } + def pick_up + "♥#{@contents}🤍" + end + end + + sig { returns(String) } + def befriend + CompanionCube.pack('').pick_up + end + + # Sorbet은 추상 클래스와 인터페이스를 지원합니다. 모든 + # 구체적인 클래스와 구현이 실제로 호환되는 + # 서명으로 필요한 메서드를 정의하는지 확인할 수 있습니다. + + # 추상 클래스는 다음과 같습니다: + + class WorkflowStep + extend T::Sig + + # 상속 도우미를 가져옵니다. + extend T::Helpers + + # 이 클래스를 추상으로 표시합니다. 즉, `.new`로 인스턴스화할 수 없지만 + # 여전히 서브클래스화할 수 있습니다. + abstract! + + sig { params(args: T::Array[String]).void } + def run(args) + pre_hook + execute(args) + post_hook + end + + # 이것은 추상 메서드이므로 서브클래스에서 _반드시_ + # 구현해야 합니다. Sorbet에 알리려면 빈 메서드에 + # `abstract`가 있는 서명을 추가하십시오. + # + # 이 메서드의 구현이 실제로 런타임에 호출되면 + # `NotImplementedError`가 발생합니다. + sig { abstract.params(args: T::Array[String]).void } + def execute(args); end + + # 다음 비추상 메서드는 서브클래스에서 구현할 수 있지만 + # 선택 사항입니다. + + sig { void } + def pre_hook; end + + sig { void } + def post_hook; end + end + + class Configure < WorkflowStep + extend T::Sig + + sig { void } + def pre_hook + puts 'Configuring...' + end + + # 추상 메서드를 구현하려면 서명에 `override`를 표시하십시오. + sig { override.params(args: T::Array[String]).void } + def execute(args) + # ... + end + end + + # 그리고 인터페이스는 다음과 같습니다: + + module Queue + extend T::Sig + + # 상속 도우미를 가져옵니다. + extend T::Helpers + + # 이 모듈을 인터페이스로 표시합니다. 그러면 다음 제한 사항이 추가됩니다: + # 1. 모든 메서드는 추상이어야 합니다. + # 2. private 또는 protected 메서드를 가질 수 없습니다. + interface! + + sig { abstract.params(num: Integer).void } + def push(num); end + + sig { abstract.returns(T.nilable(Integer)) } + def pop; end + end + + class PriorityQueue + extend T::Sig + + # 이 클래스가 인터페이스를 구현한다는 것을 Sorbet에 알리기 위해 + # 인터페이스를 포함합니다. Sorbet은 암시적으로 구현된 인터페이스 + # ("덕 타이핑"이라고도 함)를 지원하지 않습니다. + include Queue + + sig { void } + def initialize + @items = T.let([], T::Array[Integer]) + end + + # Queue 인터페이스의 추상 메서드를 구현합니다. `override`를 + # 사용하는 것을 잊지 마십시오! + + sig { override.params(num: Integer).void } + def push(num) + @items << num + @items.sort! + end + + sig { override.returns(T.nilable(Integer)) } + def pop + @items.shift + end + end + + # 모듈에서 클래스 메서드를 얻기 위해 `included` 후크를 사용하는 경우 + # 타입 검사를 위해 `mixes_in_class_methods`를 사용해야 합니다. + + module Mixin + extend T::Helpers + interface! + + module ClassMethods + extend T::Sig + + sig { void } + def whisk + 'fskfskfsk' + end + end + + mixes_in_class_methods(ClassMethods) + end + + class EggBeater + include Mixin + end + + EggBeater.whisk # Meringue! +end + +module EscapeHatches + extend T::Sig + + # Ruby는 매우 동적인 언어이며 때로는 Sorbet이 이미 + # 사실이라고 알고 있는 속성을 추론할 수 없습니다. Sorbet이 + # 안전성을 증명할 수 있도록 코드를 다시 작성하는 방법이 있지만, + # 이러한 "탈출구"를 사용하여 Sorbet에서 "벗어날" 수도 있습니다. + + # `T.nilable`을 사용하기 시작하면 Sorbet은 nil을 처리하지 않는 + # _모든_ 곳을 알려주기 시작합니다. 때로는 값이 nil이 될 수 없다는 것을 + # 알고 있지만 Sorbet이 증명할 수 있도록 시그를 수정하는 것이 + # 실용적이지 않은 경우가 있습니다. 이 경우 `T.must`를 사용할 수 있습니다. + sig { params(maybe_str: T.nilable(String)).returns(String) } + def no_nils_here(maybe_str) + # maybe_str이 실제로 nil이면 런타임에 오류가 발생합니다. + str = T.must(maybe_str) + str.downcase + end + + # 더 일반적으로, 값이 특정 타입이어야 한다는 것을 알고 있는 경우 + # `T.cast`를 사용할 수 있습니다. + sig do + params( + str_or_ary: T.any(String, T::Array[String]), + idx_or_range: T.any(Integer, T::Range[Integer]), + ).returns(T::Array[String]) + end + def slice2(str_or_ary, idx_or_range) + # 어떤 이유로든 문자열에서 개별 문자 또는 + # 배열에서 하위 배열을 원한다고 가정해 봅시다. 다른 옵션은 허용되지 않습니다. + if str_or_ary.is_a?(String) + # 여기서 `idx_or_range`는 단일 인덱스여야 함을 알고 있습니다. 그렇지 않으면 + # 런타임에 오류가 발생합니다. + idx = T.cast(idx_or_range, Integer) + [str_or_ary.chars.fetch(idx)] + else + # 여기서 `idx_or_range`는 범위여야 함을 알고 있습니다. 그렇지 않으면 + # 런타임에 오류가 발생합니다. + range = T.cast(idx_or_range, T::Range[Integer]) + str_or_ary.slice(range) || [] + end + end + + # 메서드가 존재하지만 Sorbet이 모르는 경우 `T.unsafe`를 + # 사용하여 Sorbet이 호출하도록 할 수 있습니다. 이것을 "안전하지 않은 + # 메서드 호출"로 생각하는 경향이 있지만, `T.unsafe`는 전체 + # 표현식이 아닌 수신자에서 호출됩니다. + sig { params(count: Integer).returns(Date) } + def the_future(count) + # Sorbet이 찾을 수 없는 추가 날짜 도우미를 정의했다고 가정해 봅시다. + # 따라서 `2.decades`는 ActiveSupport의 `(2*10).years`와 효과적으로 동일합니다. + Date.today + T.unsafe(count).decades + end + + # 이것이 암시적 `self`의 메서드인 경우 `T.unsafe`를 사용하려면 + # 명시적으로 만들어야 합니다. + sig { params(count: Integer).returns(Date) } + def the_past(count) + # 메타프로그래밍이 `Time.new`에 대한 `now` 도우미 메서드를 + # 정의한다고 가정해 봅시다. 일반적으로 다음과 같이 보입니다: + # + # now - 1234 + # + T.unsafe(self).now - 1234 + end + + # Sorbet에는 `T.untyped`라는 특별한 타입이 있습니다. 이 타입의 + # 모든 값에 대해 Sorbet은 모든 메서드 인수에 사용되고 + # 모든 메서드 호출을 수신하도록 허용합니다. + + sig { params(num: Integer, anything: T.untyped).returns(T.untyped) } + def nothing_to_see_here(num, anything) + anything.digits # 정수인가... + anything.upcase # ...아니면 문자열인가? + + # Sorbet은 타입이 지정되지 않았기 때문에 이 반환 값에 대해 + # 아무것도 추론할 수 없습니다. + BasicObject.new + end + + def see_here + # 실제로는 nil입니다! 런타임에 충돌하지만 Sorbet은 허용합니다. + nothing_to_see_here(1, nil) + end + + # 시그가 없는 메서드의 경우 Sorbet은 각 인수와 + # 반환 값의 타입을 `T.untyped`로 추론합니다. +end + +# 다음 타입은 공식적으로 문서화되지 않았지만 여전히 유용합니다. +# 실험적이거나, 더 이상 사용되지 않거나, 지원되지 않을 수 있습니다. + +module ValueSet + extend T::Sig + + # Ruby의 일반적인 패턴은 옵션 집합에서 하나의 값을 허용하는 + # 메서드를 갖는 것입니다. 특히 Sorbet을 처음 시작할 때 + # 코드를 리팩토링하여 `T::Enum`을 사용하는 것이 실용적이지 않을 수 있습니다. + # 이 경우 `T.enum`을 사용할 수 있습니다. + # + # 참고: Sorbet은 값 자체를 추적하지 않기 때문에 정적으로 + # 확인할 수 없습니다. + sig do + params( + data: T::Array[Numeric], + shape: T.enum([:circle, :square, :triangle]) + ).void + end + def plot_points(data, shape: :circle) + data.each_with_index do |y, x| + Kernel.puts "#{x}: #{y}" + end + end +end + +module Generics + extend T::Sig + + # 제네릭은 메서드 타입이 포함된 데이터에 따라 변경되는 + # 클래스나 메서드 타입이 인수에 따라 변경되는 메서드가 있는 + # 경우에 유용합니다. + + # 제네릭 메서드는 `type_parameters`를 사용하여 타입 변수를 선언하고 + # `T.type_parameter`를 사용하여 다시 참조합니다. + sig do + type_parameters(:element) + .params( + element: T.type_parameter(:element), + count: Integer, + ).returns(T::Array[T.type_parameter(:element)]) + end + def repeat_value(element, count) + count.times.each_with_object([]) do |elt, ary| + ary << elt + end + end + + sig do + type_parameters(:element) + .params( + count: Integer, + block: T.proc.returns(T.type_parameter(:element)), + ).returns(T::Array[T.type_parameter(:element)]) + end + def repeat_cached(count, &block) + elt = block.call + ary = [] + count.times do + ary << elt + end + ary + end + + # 제네릭 클래스는 `T::Generic.type_member`를 사용하여 일반 + # 타입 이름과 같은 타입 변수를 정의합니다. + class BidirectionalHash + extend T::Sig + extend T::Generic + + Left = type_member + Right = type_member + + sig { void } + def initialize + @left_hash = T.let({}, T::Hash[Left, Right]) + @right_hash = T.let({}, T::Hash[Right, Left]) + end + + # 아래 메서드가 작동하도록 충분히 구현합니다. + + sig { params(lkey: Left).returns(T::Boolean) } + def lhas?(lkey) + @left_hash.has_key?(lkey) + end + + sig { params(rkey: Right).returns(T.nilable(Left)) } + def rget(rkey) + @right_hash[rkey] + end + end + + # 제네릭 타입을 특수화하려면 대괄호를 사용하십시오. + sig do + params( + options: BidirectionalHash[Symbol, Integer], + choice: T.any(Symbol, Integer), + ).returns(T.nilable(String)) + end + def lookup(options, choice) + case choice + when Symbol + options.lhas?(choice) ? choice.to_s : nil + when Integer + options.rget(choice).to_s + else + T.absurd(choice) + end + end + + # 상속을 통해 특수화하려면 `fixed`로 `type_member`를 다시 + # 선언하십시오. + class Options < BidirectionalHash + Left = type_member(fixed: Symbol) + Right = type_member(fixed: Integer) + end + + sig do + params( + options: Options, + choice: T.any(Symbol, Integer), + ).returns(T.nilable(String)) + end + def lookup2(options, choice) + lookup(options, choice) + end + + # `type_member`에 추가할 수 있는 다른 분산 주석이 있지만 + # 거의 사용되지 않습니다. +end +``` + +## 추가 자료 + +- [공식 문서](https://sorbet.org/docs/overview) +- [sorbet.run](https://sorbet.run) - 플레이그라운드 diff --git a/ko/sql.md b/ko/sql.md new file mode 100644 index 0000000000..4e29880d2c --- /dev/null +++ b/ko/sql.md @@ -0,0 +1,159 @@ +--- +name: SQL +filename: learnsql.sql +contributors: + - ["Bob DuCharme", "http://bobdc.com/"] + - ["Th3G33k", "https://github.com/Th3G33k"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +구조적 쿼리 언어(SQL)는 테이블 집합에 저장된 데이터베이스를 만들고 작업하기 위한 [ISO/IEC 9075](https://www.iso.org/standard/63555.html) 표준 언어입니다. 구현은 일반적으로 언어에 자체 확장을 추가합니다. [다양한 SQL 구현 비교](http://troels.arvin.dk/db/rdbms/)는 제품 차이점에 대한 좋은 참조 자료입니다. + +구현은 일반적으로 여기에 표시된 명령을 대화식으로 입력할 수 있는 명령줄 프롬프트를 제공하며, 스크립트 파일에 저장된 일련의 명령을 실행하는 방법도 제공합니다. (대화형 프롬프트가 끝났음을 보여주는 것은 표준화되지 않은 좋은 예입니다. 대부분의 SQL 구현은 QUIT, EXIT 또는 둘 다 키워드를 지원합니다.) + +이러한 샘플 명령 중 일부는 [GitHub](https://github.com/datacharmer/test_db)에서 사용할 수 있는 [MySQL 직원 샘플 데이터베이스](https://dev.mysql.com/doc/employee/en/)가 이미 로드되었다고 가정합니다. GitHub 파일은 가상 회사의 직원에 대한 데이터 테이블을 만들고 채우는 아래 관련 명령과 유사한 명령 스크립트입니다. 이러한 스크립트를 실행하는 구문은 사용 중인 SQL 구현에 따라 다릅니다. 운영 체제 프롬프트에서 실행하는 유틸리티가 일반적입니다. + + +```sql +-- 주석은 두 개의 하이픈으로 시작합니다. 각 명령은 세미콜론으로 끝납니다. + +/* +여러 줄 주석 +*/ + +-- SQL은 키워드에 대해 대소문자를 구분하지 않습니다. 여기 샘플 명령은 +-- 데이터베이스, 테이블 및 열 이름과 구별하기 쉽도록 +-- 대문자로 표기하는 관례를 따릅니다. + +-- 데이터베이스 생성 및 삭제. 데이터베이스 및 테이블 이름은 대소문자를 구분합니다. +CREATE DATABASE someDatabase; +DROP DATABASE someDatabase; + +-- 사용 가능한 데이터베이스 나열. +SHOW DATABASES; + +-- 특정 기존 데이터베이스 사용. +USE employees; + +-- 현재 데이터베이스의 departments 테이블에서 모든 행과 열 선택. +-- 기본 활동은 인터프리터가 화면에 결과를 스크롤하는 것입니다. +SELECT * FROM departments; + +-- departments 테이블에서 모든 행을 검색하지만, +-- dept_no 및 dept_name 열만 검색합니다. +-- 명령을 여러 줄로 나누는 것은 괜찮습니다. +SELECT dept_no, + dept_name FROM departments; + +-- 모든 departments 열을 검색하지만 5개의 행만 검색합니다. +SELECT * FROM departments LIMIT 5; + +-- departments 테이블에서 dept_name 값이 'en' 부분 문자열을 +-- 포함하는 dept_name 열 값 검색. +SELECT dept_name FROM departments WHERE dept_name LIKE '%en%'; + +-- departments 테이블에서 dept_name 열이 'S'로 시작하고 +-- 그 뒤에 정확히 4개의 문자가 있는 모든 열 검색. +SELECT * FROM departments WHERE dept_name LIKE 'S____'; + +-- titles 테이블에서 title 값을 선택하지만 중복은 표시하지 않습니다. +SELECT DISTINCT title FROM titles; + +-- 위와 동일하지만 title 값으로 정렬됩니다(대소문자 구분). +-- 순서는 ASC(오름차순) 또는 DESC(내림차순)를 추가하여 지정할 수 있습니다. +-- 생략하면 기본적으로 오름차순으로 정렬됩니다. +SELECT DISTINCT title FROM titles ORDER BY title ASC; + +-- 비교 연산자(=, >, <, >=, <=, <>) 및 +-- 조건부 키워드(AND, OR)를 사용하여 쿼리를 구체화합니다. +SELECT * FROM departments WHERE dept_no = 'd001' OR dept_no = 'd002'; + +-- 위와 동일. +SELECT * FROM departments WHERE dept_no IN ('d001', 'd002'); + +-- 위의 반대. +SELECT * FROM departments WHERE dept_no NOT IN ('d001', 'd002'); + +-- 주어진 범위에서 선택. +SELECT * from departments WHERE dept_no BETWEEN 'd001' AND 'd002'; + +-- departments 테이블의 행 수 표시. +SELECT COUNT(*) FROM departments; + +-- dept_name 값의 부분 문자열로 'en'을 포함하는 +-- departments 테이블의 행 수 표시. +SELECT COUNT(*) FROM departments WHERE dept_name LIKE '%en%'; + +-- 집계 함수는 GROUP BY와 함께 사용하여 값 집합에서 +-- 값을 계산할 수 있습니다. 가장 일반적으로 사용되는 함수는 다음과 같습니다: +-- MIN(), MAX(), COUNT(), SUM(), AVG(). +-- HAVING을 사용하여 집계된 값으로 행을 필터링합니다. + +-- 부서 번호별로 총 직원 수를 검색하며, +-- 직원 수가 100명 이상인 조건을 만족해야 합니다. +SELECT dept_no, COUNT(dept_no) FROM dept_emp GROUP BY dept_no +HAVING COUNT(dept_no) > 100; + +-- 선택적 키워드 AS를 사용하는 별칭은 열/테이블 이름에 사용할 수 있습니다. +SELECT COUNT(A.*) AS total_employees, COUNT(B.*) total_departments +FROM employees AS A, departments B; + +-- 일반적인 날짜 형식은 "yyyy-mm-dd"입니다. +-- 그러나 구현, 운영 체제 및 세션의 로캘에 따라 다를 수 있습니다. +SELECT * FROM dept_manager WHERE from_date >= '1990-01-01'; + +-- 여러 테이블의 정보 JOIN: titles 테이블은 +-- 누가 어떤 직책을 가졌는지, 직원 번호별로, +-- 언제부터 언제까지인지 보여줍니다. 이 정보를 검색하지만, +-- 직원 번호 대신 직원 번호를 상호 참조로 사용하여 +-- employees 테이블에서 각 직원의 이름과 성을 +-- 가져옵니다. (그리고 10개의 행만 가져옵니다.) + +SELECT employees.first_name, employees.last_name, + titles.title, titles.from_date, titles.to_date +FROM titles INNER JOIN employees ON + employees.emp_no = titles.emp_no LIMIT 10; + +-- 여러 SELECT의 결과 결합. +-- UNION은 고유한 행을 선택하고, UNION ALL은 모든 행을 선택합니다. +SELECT * FROM departments WHERE dept_no = 'd001' +UNION +SELECT * FROM departments WHERE dept_no = 'd002'; + +-- SQL 구문 순서는 다음과 같습니다: +-- SELECT _ FROM _ JOIN _ ON _ WHERE _ GROUP BY _ HAVING _ ORDER BY _ UNION + +-- 모든 데이터베이스의 모든 테이블 나열. 구현은 일반적으로 +-- 현재 사용 중인 데이터베이스로 이 작업을 수행하는 자체 바로 가기 명령을 제공합니다. +SELECT * FROM INFORMATION_SCHEMA.TABLES +WHERE TABLE_TYPE='BASE TABLE'; + +-- 현재 사용 중인 데이터베이스에 대해 표시된 두 개의 열이 있는 +-- tablename1이라는 테이블을 만듭니다. 데이터 유형과 같이 +-- 열을 지정하는 방법에 대한 다른 많은 옵션이 있습니다. +CREATE TABLE tablename1 (fname VARCHAR(20), lname VARCHAR(20)); + +-- tablename1 테이블에 데이터 행 삽입. 이 테이블이 +-- 이러한 값을 적절한 것으로 받아들이도록 정의되었다고 가정합니다. +INSERT INTO tablename1 VALUES('Richard','Mutt'); + +-- tablename1에서 lname 값이 'Mutt'인 모든 행에 대해 +-- fname 값을 'John'으로 변경합니다. +UPDATE tablename1 SET fname='John' WHERE lname='Mutt'; + +-- tablename1 테이블에서 lname 값이 'M'으로 시작하는 +-- 행 삭제. +DELETE FROM tablename1 WHERE lname LIKE 'M%'; + +-- tablename1 테이블에서 모든 행을 삭제하고 빈 테이블을 남깁니다. +DELETE FROM tablename1; + +-- 전체 tablename1 테이블 제거. +DROP TABLE tablename1; +``` + +## 더 읽을거리 + +* [Codecademy - SQL](https://www.codecademy.com/learn/learn-sql) "하면서 배우기" 형식의 SQL에 대한 좋은 소개. +* [Database System Concepts](https://www.db-book.com) 책의 3장 - SQL 소개에는 SQL 개념에 대한 심층적인 설명이 있습니다. diff --git a/ko/standard-ml.md b/ko/standard-ml.md new file mode 100644 index 0000000000..3613230edd --- /dev/null +++ b/ko/standard-ml.md @@ -0,0 +1,485 @@ +--- +name: "Standard ML" +filename: standardml.sml +contributors: + - ["Simon Shine", "https://simonshine.dk/"] + - ["David Pedersen", "https://github.com/davidpdrsn"] + - ["James Baker", "http://www.jbaker.io/"] + - ["Leo Zovic", "http://langnostic.inaimathi.ca/"] + - ["Chris Wilson", "http://sencjw.com/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Standard ML은 타입 추론과 일부 부수 효과가 있는 함수형 프로그래밍 언어입니다. Standard ML을 배우는 데 어려운 부분 중 일부는 재귀, 패턴 매칭, 타입 추론(올바른 타입을 추측하지만 암시적 타입 변환은 절대 허용하지 않음)입니다. Standard ML은 변수를 업데이트할 수 있는 참조를 포함하여 Haskell과 구별됩니다. + +```ocaml +(* Standard ML의 주석은 (*로 시작하고 *)로 끝납니다. 주석은 + 중첩될 수 있습니다. 즉, 모든 (* 태그는 *) 태그로 끝나야 합니다. 이 주석은 + 예를 들어 두 개의 중첩된 주석을 포함합니다. *) + +(* Standard ML 프로그램은 선언으로 구성됩니다. 예: 값 선언: *) +val rent = 1200 +val phone_no = 5551337 +val pi = 3.14159 +val negative_number = ~15 (* 예, 단항 마이너스는 '물결표' 기호를 사용합니다 *) + +(* 선택적으로 타입을 명시적으로 선언할 수 있습니다. ML이 + 값의 타입을 자동으로 파악하므로 필수는 아닙니다. *) +val diameter = 7926 : int +val e = 2.718 : real +val name = "Bobby" : string + +(* 그리고 마찬가지로 중요한 함수: *) +fun is_large(x : int) = if x > 37 then true else false + +(* 부동 소수점 숫자는 "실수"라고 합니다. *) +val tau = 2.0 * pi (* 두 실수를 곱할 수 있습니다 *) +val twice_rent = 2 * rent (* 두 정수를 곱할 수 있습니다 *) +(* val meh = 1.25 * 10 *) (* 하지만 정수와 실수를 곱할 수는 없습니다 *) +val yeh = 1.25 * (Real.fromInt 10) (* ... 명시적으로 변환하지 않는 한 + 하나 또는 다른 것 *) + +(* +, - 및 *는 오버로드되어 int와 real 모두에 대해 작동합니다. *) +(* 나눗셈은 별도의 연산자가 있으므로 동일하다고 말할 수 없습니다: *) +val real_division = 14.0 / 4.0 (* 3.5를 제공 *) +val int_division = 14 div 4 (* 3을 제공, 버림 *) +val int_remainder = 14 mod 4 (* 2를 제공, 3*4 = 12이므로 *) + +(* ~는 실제로 때때로 함수입니다(예: 변수 앞에 놓을 때) *) +val negative_rent = ~(rent) (* rent가 "real"인 경우에도 작동했을 것입니다 *) + +(* 불리언 및 불리언 연산자도 있습니다 *) +val got_milk = true +val got_bread = false +val has_breakfast = got_milk andalso got_bread (* 'andalso'는 연산자입니다 *) +val has_something = got_milk orelse got_bread (* 'orelse'는 연산자입니다 *) +val is_sad = not(has_something) (* not은 함수입니다 *) + +(* 많은 값은 등호 연산자 = 및 <>를 사용하여 비교할 수 있습니다 *) +val pays_same_rent = (rent = 1300) (* false *) +val is_wrong_phone_no = (phone_no <> 5551337) (* false *) + +(* 연산자 <>는 대부분의 다른 언어에서 !=라고 부르는 것입니다. *) +(* 'andalso'와 'orelse'는 많은 다른 언어에서 &&와 ||라고 합니다. *) + +(* 실제로 위의 괄호 대부분은 불필요합니다. 위에서 언급한 + 몇 가지를 다른 방식으로 말하는 몇 가지 방법이 있습니다: *) +fun is_large x = x > 37 (* 위의 괄호는 ': int' 때문에 필요했습니다 *) +val is_sad = not has_something +val pays_same_rent = rent = 1300 (* 혼란스러워 보이지만 작동합니다 *) +val is_wrong_phone_no = phone_no <> 5551337 +val negative_rent = ~rent (* ~ rent (공백 참고)도 작동합니다 *) + +(* 괄호는 주로 그룹화할 때 필요합니다: *) +val some_answer = is_large (5 + 5) (* 괄호가 없으면 이것은 깨집니다! *) +(* val some_answer = is_large 5 + 5 *) (* (is_large 5) + 5로 읽습니다. 나쁨! *) + + +(* 불리언, 정수 및 실수 외에도 Standard ML에는 문자 및 문자열도 있습니다: *) +val foo = "Hello, World!\n" (* \n은 줄 바꿈의 이스케이프 시퀀스입니다 *) +val one_letter = #"a" (* 그 펑키한 구문은 단지 한 문자, a입니다 *) + +val combined = "Hello " ^ "there, " ^ "fellow!\n" (* 문자열 연결 *) + +val _ = print foo (* 인쇄할 수 있습니다. 우리는 이 계산의 결과에 + 관심이 없으므로 버립니다. *) +val _ = print combined +(* val _ = print one_letter *) (* 이 방법으로는 문자열만 인쇄할 수 있습니다 *) + + +val bar = [ #"H", #"e", #"l", #"l", #"o" ] (* SML에는 리스트도 있습니다! *) +(* val _ = print bar *) (* 불행히도 리스트는 문자열과 같지 않습니다 *) + +(* 다행히 변환할 수 있습니다. String은 라이브러리이고 implode와 size는 + 해당 라이브러리에서 사용할 수 있는 함수이며 문자열을 인수로 사용합니다. *) +val bob = String.implode bar (* "Hello"를 제공 *) +val bob_char_count = String.size bob (* 5를 제공 *) +val _ = print (bob ^ "\n") (* 좋은 측정을 위해 줄 바꿈 추가 *) + +(* 모든 종류의 리스트를 가질 수 있습니다 *) +val numbers = [1, 3, 3, 7, 229, 230, 248] (* : int list *) +val names = [ "Fred", "Jane", "Alice" ] (* : string list *) + +(* 리스트의 리스트도 가능 *) +val groups = [ [ "Alice", "Bob" ], + [ "Huey", "Dewey", "Louie" ], + [ "Bonnie", "Clyde" ] ] (* : string list list *) + +val number_count = List.length numbers (* 7을 제공 *) + +(* :: 연산자("cons 연산자"라고 함, Lisp에서 알려짐)를 사용하여 + 동일한 종류의 리스트 앞에 단일 값을 넣을 수 있습니다. *) +val more_numbers = 13 :: numbers (* [13, 1, 3, 3, 7, ...]을 제공 *) +val more_groups = ["Batman","Superman"] :: groups + +(* 동일한 종류의 리스트는 @ ("append") 연산자를 사용하여 추가할 수 있습니다 *) +val guest_list = [ "Mom", "Dad" ] @ [ "Aunt", "Uncle" ] + +(* 이것은 "cons" 연산자로 수행할 수 있었습니다. 왼쪽 피연산자는 + 요소여야 하고 오른쪽 피연산자는 해당 요소의 리스트여야 하므로 + 까다롭습니다. *) +val guest_list = "Mom" :: "Dad" :: [ "Aunt", "Uncle" ] +val guest_list = "Mom" :: ("Dad" :: ("Aunt" :: ("Uncle" :: []))) + +(* 동일한 종류의 리스트가 많은 경우 모두 연결할 수 있습니다 *) +val everyone = List.concat groups (* [ "Alice", "Bob", "Huey", ... ] *) + +(* 리스트는 (유한한) 수의 값을 포함할 수 있습니다 *) +val lots = [ 5, 5, 5, 6, 4, 5, 6, 5, 4, 5, 7, 3 ] (* 여전히 int list일 뿐입니다 *) + +(* 리스트는 한 종류의 것만 포함할 수 있습니다... *) +(* val bad_list = [ 1, "Hello", 3.14159 ] : ??? list *) + + +(* 반면에 튜플은 고정된 수의 다른 것을 포함할 수 있습니다 *) +val person1 = ("Simon", 28, 3.14159) (* : string * int * real *) + +(* 리스트 안에 튜플을, 튜플 안에 리스트를 가질 수도 있습니다 *) +val likes = [ ("Alice", "ice cream"), + ("Bob", "hot dogs"), + ("Bob", "Alice") ] (* : (string * string) list *) + +val mixup = [ ("Alice", 39), + ("Bob", 37), + ("Eve", 41) ] (* : (string * int) list *) + +val good_bad_stuff = + (["ice cream", "hot dogs", "chocolate"], + ["liver", "paying the rent" ]) (* : string list * string list *) + + +(* 레코드는 이름 있는 슬롯이 있는 튜플입니다 *) + +val rgb = { r=0.23, g=0.56, b=0.91 } (* : {b:real, g:real, r:real} *) + +(* 미리 슬롯을 선언할 필요가 없습니다. 슬롯 이름이 다른 레코드는 + 슬롯 값 유형이 일치하더라도 다른 유형으로 간주됩니다. + 예를 들어... *) + +val Hsl = { H=310.3, s=0.51, l=0.23 } (* : {H:real, l:real, s:real} *) +val Hsv = { H=310.3, s=0.51, v=0.23 } (* : {H:real, s:real, v:real} *) + +(* ...`Hsv = Hsl` 또는 `rgb = Hsl`을 평가하려고 하면 유형 오류가 + 발생합니다. 모두 세 개의 슬롯으로 구성된 `real` 레코드이지만, + 각각 적어도 일부 슬롯에 대해 다른 이름을 가지고 있습니다. *) + +(* 해시 표기법을 사용하여 튜플에서 값을 가져올 수 있습니다. *) + +val H = #H Hsv (* : real *) +val s = #s Hsl (* : real *) + +(* 함수! *) +fun add_them (a, b) = a + b (* 두 숫자를 더하는 간단한 함수 *) +val test_it = add_them (3, 4) (* 7을 제공 *) + +(* 더 큰 함수는 일반적으로 가독성을 위해 여러 줄로 나뉩니다 *) +fun thermometer temp = + if temp < 37 + then "Cold" + else if temp > 37 + then "Warm" + else "Normal" + +val test_thermo = thermometer 40 (* "Warm"을 제공 *) + +(* if-문장은 실제로는 문/선언이 아닌 표현식입니다. + 함수 본문은 하나의 표현식만 포함할 수 있습니다. 하지만 함수가 + 한 가지 이상의 일을 하도록 하는 몇 가지 트릭이 있습니다. *) + +(* 함수는 결과의 일부로 자신을 호출할 수 있습니다(재귀!) *) +fun fibonacci n = + if n = 0 then 0 else (* 기본 사례 *) + if n = 1 then 1 else (* 기본 사례 *) + fibonacci (n - 1) + fibonacci (n - 2) (* 재귀 사례 *) + +(* 때로는 재귀를 직접 함수를 평가하여 가장 잘 이해할 수 있습니다: + + fibonacci 4 + ~> fibonacci (4 - 1) + fibonacci (4 - 2) + ~> fibonacci 3 + fibonacci 2 + ~> (fibonacci (3 - 1) + fibonacci (3 - 2)) + fibonacci 2 + ~> (fibonacci 2 + fibonacci 1) + fibonacci 2 + ~> ((fibonacci (2 - 1) + fibonacci (2 - 2)) + fibonacci 1) + fibonacci 2 + ~> ((fibonacci 1 + fibonacci 0) + fibonacci 1) + fibonacci 2 + ~> ((1 + fibonacci 0) + fibonacci 1) + fibonacci 2 + ~> ((1 + 0) + fibonacci 1) + fibonacci 2 + ~> (1 + fibonacci 1) + fibonacci 2 + ~> (1 + 1) + fibonacci 2 + ~> 2 + fibonacci 2 + ~> 2 + (fibonacci (2 - 1) + fibonacci (2 - 2)) + ~> 2 + (fibonacci (2 - 1) + fibonacci (2 - 2)) + ~> 2 + (fibonacci 1 + fibonacci 0) + ~> 2 + (1 + fibonacci 0) + ~> 2 + (1 + 0) + ~> 2 + 1 + ~> 3 이 정의에 따르면 4번째 피보나치 수입니다 + + *) + +(* 함수는 참조할 수 있는 변수를 변경할 수 없습니다. + 동일한 이름을 가진 새 변수로 일시적으로 가릴 수만 있습니다. + 이런 의미에서 변수는 실제로는 상수이며 + 재귀를 다룰 때만 변수처럼 동작합니다. + 이러한 이유로 변수는 값 바인딩이라고도 합니다. + 예: *) + +val x = 42 +fun answer(question) = + if question = "What is the meaning of life, the universe and everything?" + then x + else raise Fail "I'm an exception. Also, I don't know what the answer is." +val x = 43 +val hmm = answer "What is the meaning of life, the universe and everything?" +(* 이제 hmm은 값 42를 가집니다. 이것은 함수 answer가 + 자체 함수 정의 전에 표시되었던 x의 복사본을 참조하기 때문입니다. *) + + +(* 함수는 하나의 튜플을 인수로 사용하여 여러 인수를 받을 수 있습니다: *) +fun solve2 (a : real, b : real, c : real) = + ((~b + Math.sqrt(b * b - 4.0 * a * c)) / (2.0 * a), + (~b - Math.sqrt(b * b - 4.0 * a * c)) / (2.0 * a)) + +(* 때로는 동일한 계산이 여러 번 수행됩니다. + 결과를 저장하고 처음으로 재사용하는 것이 합리적입니다. + "let-bindings"를 사용할 수 있습니다: *) +fun solve2 (a : real, b : real, c : real) = + let val discr = b * b - 4.0 * a * c + val sqr = Math.sqrt discr + val denom = 2.0 * a + in ((~b + sqr) / denom, + (~b - sqr) / denom) + end + + +(* 패턴 매칭은 함수형 프로그래밍의 펑키한 부분입니다. + if-문장의 대안입니다. 피보나치 함수는 다음과 같이 다시 작성할 수 있습니다: *) +fun fibonacci 0 = 0 (* 기본 사례 *) + | fibonacci 1 = 1 (* 기본 사례 *) + | fibonacci n = fibonacci (n - 1) + fibonacci (n - 2) (* 재귀 사례 *) + +(* 패턴 매칭은 튜플, 리스트 및 레코드와 같은 복합 유형에서도 가능합니다. + "fun solve2 (a, b, c) = ..."를 작성하는 것은 사실상 + solve2가 인수로 받는 하나의 세 튜플에 대한 패턴 매치입니다. + 마찬가지로, 덜 직관적이지만, 그 안에 있는 요소로 구성된 리스트에 대해 + (리스트의 시작부터만) 매치할 수 있습니다. *) +fun first_elem (x::xs) = x +fun second_elem (x::y::xs) = y +fun evenly_positioned_elems (odd::even::xs) = even::evenly_positioned_elems xs + | evenly_positioned_elems [odd] = [] (* 기본 사례: 버리기 *) + | evenly_positioned_elems [] = [] (* 기본 사례 *) + +(* case 표현식은 패턴 매치하고 값을 반환하는 데에도 사용할 수 있습니다 *) +datatype temp = + C of real + | F of real + +(* 새로운 C 온도 값 선언... + val t: temp = C 45.0 *) + +fun temp_to_f t = + case t of + C x => x * (9.0 / 5.0) + 32.0 + | F x => x + +(* 레코드에 대해 매칭할 때 슬롯 이름을 사용해야 하며, + 레코드의 모든 슬롯을 바인딩해야 합니다. 슬롯의 순서는 중요하지 않습니다. *) + +fun rgbToTup {r, g, b} = (r, g, b) (* fn : {b:'a, g:'b, r:'c} -> 'c * 'b * 'a *) +fun mixRgbToTup {g, b, r} = (r, g, b) (* fn : {b:'a, g:'b, r:'c} -> 'c * 'b * 'a *) + +(* {r=0.1, g=0.2, b=0.3}으로 호출하면 위의 함수 중 하나가 + (0.1, 0.2, 0.3)을 반환합니다. 그러나 {r=0.1, g=0.2, b=0.3, a=0.4}로 + 호출하면 유형 오류가 발생합니다. *) + +(* 고차 함수: 함수는 다른 함수를 인수로 받을 수 있습니다. + 함수는 다른 종류의 값일 뿐이며, 함수는 존재하기 위해 + 이름이 필요하지 않습니다. 이름 없는 함수는 "익명 함수" 또는 + 람다 표현식 또는 클로저(어휘적 범위를 가지므로)라고 합니다. *) +val is_large = (fn x => x > 37) +val add_them = fn (a,b) => a + b +val thermometer = + fn temp => if temp < 37 + then "Cold" + else if temp > 37 + then "Warm" + else "Normal" + +(* 다음은 익명 함수를 직접 사용하여 "ColdWarm"을 제공합니다 *) +val some_result = (fn x => thermometer (x - 5) ^ thermometer (x + 5)) 37 + +(* 다음은 리스트에서 작동하는 고차 함수입니다(리스트 조합기) *) +(* map f l + l의 각 요소에 왼쪽에서 오른쪽으로 f를 적용하고, + 결과 목록을 반환합니다. *) +val readings = [ 34, 39, 37, 38, 35, 36, 37, 37, 37 ] (* 먼저 int list *) +val opinions = List.map thermometer readings (* [ "Cold", "Warm", ... ]을 제공 *) + +(* 그리고 다음은 리스트 필터링을 위한 또 다른 것입니다 *) +val warm_readings = List.filter is_large readings (* [39, 38]을 제공 *) + +(* 자신만의 고차 함수를 만들 수도 있습니다. 함수는 + "커링"하여 여러 인수를 받을 수도 있습니다. 구문적으로 이것은 + 쉼표와 주변 괄호 대신 함수 인수 사이에 공백을 추가하는 것을 + 의미합니다. *) +fun map f [] = [] + | map f (x::xs) = f(x) :: map f xs + +(* map은 유형 ('a -> 'b) -> 'a list -> 'b list를 가지며 다형성이라고 합니다. *) +(* 'a는 유형 변수라고 합니다. *) + + +(* 함수를 중위로 선언할 수 있습니다 *) +val plus = add_them (* plus는 이제 add_them과 동일한 함수와 같습니다 *) +infix plus (* plus는 이제 중위 연산자입니다 *) +val seven = 2 plus 5 (* seven은 이제 7에 바인딩됩니다 *) + +(* 함수는 선언되기 전에 중위로 만들 수도 있습니다 *) +infix minus +fun x minus y = x - y (* 인수가 무엇인지 보기가 조금 어려워집니다 *) +val four = 8 minus 4 (* four는 이제 4에 바인딩됩니다 *) + +(* 중위 함수/연산자는 'op'로 접두사로 만들 수 있습니다 *) +val n = op + (5, 5) (* n은 이제 10입니다 *) + +(* 'op'는 고차 함수와 결합할 때 유용합니다. 왜냐하면 그들은 + 연산자가 아닌 함수를 인수로 기대하기 때문입니다. 대부분의 연산자는 + 실제로 중위 함수일 뿐입니다. *) +(* foldl f init [x1, x2, ..., xn] + 반환 + f(xn, ...f(x2, f(x1, init))...) + 또는 리스트가 비어 있으면 init. *) +val sum_of_numbers = foldl op+ 0 [1, 2, 3, 4, 5] + + +(* 데이터 유형은 간단하고 복잡한 구조를 만드는 데 유용합니다 *) +datatype color = Red | Green | Blue + +(* 다음은 이들 중 하나를 인수로 받는 함수입니다 *) +fun say(col) = + if col = Red then "You are red!" else + if col = Green then "You are green!" else + if col = Blue then "You are blue!" else + raise Fail "Unknown color" + +val _ = print (say(Red) ^ "\n") + +(* 데이터 유형은 패턴 매칭과 함께 매우 자주 사용됩니다 *) +fun say Red = "You are red!" + | say Green = "You are green!" + | say Blue = "You are blue!" + +(* 세 가지 색상을 모두 지정한 후 패턴이 완전하므로 + `say _ = raise Fail "Unknown color"` 일치 암을 포함하지 않았습니다. + 그리고 패턴 매칭에서는 중복이 허용되지 않습니다. *) + + +(* 다음은 이진 트리 데이터 유형입니다 *) +datatype 'a btree = Leaf of 'a + | Node of 'a btree * 'a * 'a btree (* 세 인수 생성자 *) + +(* 다음은 이진 트리입니다 *) +val myTree = Node (Leaf 9, 8, Node (Leaf 3, 5, Leaf 7)) + +(* 그리면 다음과 같이 보일 수 있습니다... + + 8 + / \ + leaf -> 9 5 + / \ + leaf -> 3 7 <- leaf + *) + +(* 이 함수는 트리의 모든 요소의 합을 계산합니다 *) +fun count (Leaf n) = n + | count (Node (leftTree, n, rightTree)) = count leftTree + n + count rightTree + +val myTreeCount = count myTree (* myTreeCount는 이제 32에 바인딩됩니다 *) + + +(* 예외! *) +(* 예외는 예약어 'raise'를 사용하여 발생/던질 수 있습니다 *) +fun calculate_interest(n) = if n < 0.0 + then raise Domain + else n * 1.04 + +(* 예외는 "handle"을 사용하여 잡을 수 있습니다 *) +val balance = calculate_interest ~180.0 + handle Domain => ~180.0 (* balance는 이제 ~180.0 값을 가집니다 *) + +(* 일부 예외는 추가 정보를 포함합니다 *) +(* 다음은 내장 예외의 몇 가지 예입니다 *) +fun failing_function [] = raise Empty (* 빈 리스트에 사용됨 *) + | failing_function [x] = raise Fail "This list is too short!" + | failing_function [x,y] = raise Overflow (* 산술에 사용됨 *) + | failing_function xs = raise Fail "This list is too long!" + +(* 'handle'에서 패턴 매칭하여 + 특정 예외가 발생했는지 확인하거나 메시지를 가져올 수 있습니다 *) +val err_msg = failing_function [1,2] handle Fail _ => "Fail was raised" + | Domain => "Domain was raised" + | Empty => "Empty was raised" + | _ => "Unknown exception" + +(* err_msg는 이제 "Unknown exception" 값을 가집니다. Overflow가 + 패턴 중 하나로 나열되지 않았기 때문입니다. 따라서 catch-all 패턴 _이 + 사용됩니다. *) + +(* 다음과 같이 자신만의 예외를 정의할 수 있습니다 *) +exception MyException +exception MyExceptionWithMessage of string +exception SyntaxError of string * (int * int) + +(* 파일 I/O! *) +(* 파일에 멋진 시 쓰기 *) +fun writePoem(filename) = + let val file = TextIO.openOut(filename) + val _ = TextIO.output(file, "Roses are red,\nViolets are blue.\n") + val _ = TextIO.output(file, "I have a gun.\nGet in the van.\n") + in TextIO.closeOut(file) + end + +(* 파일에서 멋진 시를 문자열 목록으로 읽기 *) +fun readPoem(filename) = + let val file = TextIO.openIn filename + val poem = TextIO.inputAll file + val _ = TextIO.closeIn file + in String.tokens (fn c => c = #"\n") poem + end + +val _ = writePoem "roses.txt" +val test_poem = readPoem "roses.txt" (* [ "Roses are red,", + "Violets are blue.", + "I have a gun.", + "Get in the van." ]을 제공 *) + +(* 업데이트할 수 있는 데이터에 대한 참조를 만들 수 있습니다 *) +val counter = ref 0 (* ref 함수로 참조 생성 *) + +(* 할당 연산자로 참조에 할당 *) +fun set_five reference = reference := 5 + +(* 역참조 연산자로 참조 읽기 *) +fun equals_five reference = !reference = 5 + +(* 재귀가 복잡할 때 while 루프를 사용할 수 있습니다 *) +fun decrement_to_zero r = if !r < 0 + then r := 0 + else while !r >= 0 do r := !r - 1 + +(* 이것은 단위 값을 반환합니다 (실질적으로는 아무것도 아님, 0-튜플) *) + +(* 값을 반환하도록 허용하려면 세미콜론을 사용하여 평가를 시퀀싱할 수 있습니다 *) +fun decrement_ret x y = (x := !x - 1; y) +``` + +## 추가 학습 + +* 대화형 컴파일러(REPL) 설치, 예를 들어 + [Poly/ML](http://www.polyml.org/), + [Moscow ML](http://mosml.org), + [SML/NJ](http://smlnj.org/). +* Coursera 과정 [프로그래밍 언어](https://www.coursera.org/course/proglang) 수강. +* Larry C. Paulson의 *[ML for the Working Programmer](https://www.cl.cam.ac.uk/~lp15/MLbook/pub-details.html)* 읽기. +* [StackOverflow의 sml 태그](http://stackoverflow.com/questions/tagged/sml) 사용. +* [Exercism.io의 Standard ML 트랙](https://exercism.io/tracks/sml)에서 연습 문제 풀기. diff --git a/ko/stylus.md b/ko/stylus.md new file mode 100644 index 0000000000..be505a0a58 --- /dev/null +++ b/ko/stylus.md @@ -0,0 +1,228 @@ +--- +name: Stylus +filename: learnStylus.styl +contributors: + - ["Salomão Neto", "https://github.com/salomaosnff"] + - ["Isaac Henrique", "https://github.com/Isaachi1"] +translators: + - ["Divay Prakash", "https://github.com/divayprakash"] + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Stylus는 CSS로 컴파일되는 동적 스타일시트 전처리기 언어입니다. 웹 브라우저 간의 호환성을 깨지 않으면서 CSS에 기능을 추가하는 것을 목표로 합니다. +변수, 중첩, 믹스인, 함수 등을 사용하여 이를 수행합니다. + +Stylus 구문은 매우 유연합니다. 표준 CSS 구문을 사용하고 세미콜론(;), 콜론(:) 및 ({)과 (})를 선택적으로 생략하여 코드를 더욱 읽기 쉽게 만들 수 있습니다. + +Stylus는 새로운 스타일 옵션을 제공하지 않지만 CSS를 훨씬 더 동적으로 만들 수 있는 기능을 제공합니다. + +```scss +/* 코드 스타일 +==============================*/ + +/* Stylus에서는 키, 세미콜론 및 콜론이 선택 사항입니다. */ + +body { + background: #000; +} + +body { + background: #000 +} + +body { + background #000 +} + +body + background #000 + +body + background: #000; + +body + background: #000 + +// 한 줄 주석은 Stylus가 CSS로 컴파일될 때 제거됩니다. + +/* 여러 줄 주석은 유지됩니다. */ + + +/* 선택자 +==============================*/ + +/* 다른 요소 내의 요소 선택 */ +body { + background: #000000; + h1 { + color: #FF0000; + } +} + +/* 또는 원한다면... */ +body + background #000000 + h1 + color #FF0000 + + +/* 부모 요소 참조 가져오기 +==============================*/ +a { + color: #0088dd; + &:hover { + color: #DD8800; + } +} + + +/* 변수 +==============================*/ + + +/* + 변수의 CSS 값(예: 색상)을 저장할 수 있습니다. + 선택 사항이지만 변수 이름 앞에 $를 추가하여 + 변수를 다른 CSS 값과 구별하는 것이 좋습니다. +*/ + +$primary-color = #A3A4FF +$secondary-color = #51527F +$body-font = 'Roboto', sans-serif + +/* 스타일시트 전체에서 변수를 사용할 수 있습니다. +이제 색상을 변경하려면 한 번만 변경하면 됩니다. */ + +body + background-color $primary-color + color $secondary-color + font-family $body-font + +/* 컴파일 후: */ +body { + background-color: #A3A4FF; + color: #51527F; + font-family: 'Roboto', sans-serif; +} + +/ * +스타일시트 전체에서 색상이 나타날 때마다 색상을 변경하는 것보다 +유지 관리가 훨씬 쉽습니다. +* / + + +/* 믹스인 +==============================*/ + +/* 둘 이상의 요소에 대해 동일한 코드를 작성하고 있다는 것을 +알게 되면 해당 코드를 믹스인에 저장하고 싶을 수 있습니다. + +center() + display block + margin-left auto + margin-right auto + left 0 + right 0 + +/* 믹스인 사용 */ +body { + center() + background-color: $primary-color +} + +/* 컴파일 후: */ +div { + display: block; + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + background-color: #A3A4FF; +} + +/* 믹스인을 사용하여 약식 속성을 만들 수 있습니다. */ + +size($width, $height) + width $width + height $height + +.rectangle + size(100px, 60px) + +.square + size(40px, 40px) + +/* 믹스인을 CSS 속성으로 사용할 수 있습니다. */ +circle($ratio) + width $ratio * 2 + height $ratio * 2 + border-radius $ratio + +.ball + circle 25px + + +/* 보간 +==============================*/ + +vendor(prop, args) + -webkit-{prop} args + -moz-{prop} args + {prop} args + +border-radius() + vendor('border-radius', arguments) + +box-shadow() + vendor('box-shadow', arguments) + +button + border-radius 1px 2px / 3px 4px + + +/* 함수 +==============================*/ + +/* Stylus의 함수를 사용하면 일부 데이터를 다시 호출하는 등 다양한 작업을 수행할 수 있습니다. */ + +body { + background darken(#0088DD, 50%) // 색상 #0088DD를 50% 어둡게 합니다. +} + +/* 자신만의 함수 만들기 */ +add(a, b) + a + b + +body + padding add(10px, 5) + + +/* 조건 +==============================*/ +compare(a, b) + if a > b + bigger + else if a < b + smaller + else + equal + +compare(5, 2) // => bigger +compare(1, 5) // => smaller +compare(10, 10) // => equal + + +/* 반복 +==============================*/ + +/* +반복 루프 구문: +for [, ] in +*/ + +for $item in (1..2) /* 블록 12번 반복 */ + .col-{$item} + width ($item / 12) * 100% /* 열 번호로 행 계산 */ +``` + +이제 이 강력한 CSS 전처리기에 대해 조금 알았으니 더 동적인 스타일 시트를 만들 준비가 되었습니다. 더 자세히 알아보려면 공식 스타일러스 문서 [stylus-lang.com](https://stylus-lang.com)을 방문하십시오. diff --git a/ko/swift.md b/ko/swift.md new file mode 100644 index 0000000000..24025196e9 --- /dev/null +++ b/ko/swift.md @@ -0,0 +1,1012 @@ +--- +name: Swift +contributors: + - ["Grant Timmerman", "http://github.com/grant"] + - ["Christopher Bess", "http://github.com/cbess"] + - ["Joey Huang", "http://github.com/kamidox"] + - ["Anthony Nguyen", "http://github.com/anthonyn60"] + - ["Clayton Walker", "https://github.com/cwalk"] + - ["Fernando Valverde", "http://visualcosita.xyz"] + - ["Alexey Nazaroff", "https://github.com/rogaven"] + - ["@Samasaur1", "https://github.com/Samasaur1"] +filename: learnswift.swift +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Swift는 모든 Apple 운영 체제에서 개발하기 위해 Apple이 만든 프로그래밍 언어입니다. 오류가 있는 코드에 대해 더 탄력적이면서 Objective-C와 공존하도록 설계된 Swift는 2014년 Apple의 개발자 컨퍼런스 WWDC에서 소개되었습니다. 자동 메모리 관리, 타입 안전성 및 코드를 더 읽기 쉽고 오류가 적게 만드는 현대적인 구문을 특징으로 합니다. + +Swift는 오픈 소스이며 Linux 및 Windows에서도 실행됩니다. 이 언어는 LLVM 컴파일러로 빌드되었으며 Xcode에 포함되어 있습니다. + +공식 참조는 [docs.swift.org](https://docs.swift.org/swift-book/documentation/the-swift-programming-language)에서 제공되는 *The Swift Programming Language* 가이드입니다. 이 포괄적인 가이드는 최신 Swift 기능으로 정기적으로 업데이트되며 언어를 배우기 위한 권장 시작점입니다. + +```swift +// 모듈 가져오기 +import Foundation + +// 한 줄 주석은 //로 시작합니다. +// 여러 줄 주석은 /*로 시작하여 */로 끝납니다. +/* 중첩된 여러 줄 주석 + /* 은 */ + 허용됩니다. + */ + +// Xcode는 코드에 주석을 달고 점프 바에 나열하는 랜드마크를 지원합니다. +// MARK: 섹션 마크 +// MARK: - 구분선이 있는 섹션 마크 +// TODO: 곧 무언가 해야 함 +// FIXME: 이 코드 수정 + +//MARK: Hello, World +// Swift 3부터는 `print` 메서드를 사용하여 출력합니다. +// 자동으로 새 줄을 추가합니다. +print("Hello, world") + +// +// MARK: - 변수 +// + + +//상수를 선언하려면 `let`을, 변수를 선언하려면 `var`를 사용합니다. +let theAnswer = 42 +var theQuestion = "What is the Answer?" +theQuestion = "How many roads must a man walk down?" +theQuestion = "What is six by nine?" +// 상수를 재할당하려고 하면 컴파일 타임 오류가 발생합니다. +//theAnswer = 54 + +// 변수와 상수 모두 값을 할당하기 전에 선언할 수 있지만, +// 사용하기 전에 값을 할당해야 합니다. +let someConstant: Int +var someVariable: String +// 이 줄은 오류를 발생시킵니다. +//print(someConstant) +//print(someVariable) +someConstant = 0 +someVariable = "0" +// 이 줄은 이제 유효합니다. +print(someConstant) +print(someVariable) + +// 위에서 볼 수 있듯이 변수 타입은 자동으로 추론됩니다. +// 타입을 명시적으로 선언하려면 변수 이름 뒤에 +// 콜론으로 구분하여 작성합니다. +let aString: String = "A string" +let aDouble: Double = 0 + +// 값은 다른 타입으로 암시적으로 변환되지 않습니다. +// 원하는 타입의 인스턴스를 명시적으로 만듭니다. +let stringWithDouble = aString + String(aDouble) +let intFromDouble = Int(aDouble) + +// 문자열의 경우 문자열 보간을 사용합니다. +let descriptionString = "The value of aDouble is \(aDouble)" +// 문자열 보간 안에 모든 표현식을 넣을 수 있습니다. +let equation = "Six by nine is \(6 * 9), not 42!" +// 큰따옴표와 백슬래시 이스케이프를 피하려면 문자열 구분 기호를 변경하십시오. +let explanationString = #"The string I used was "The value of aDouble is \(aDouble)" and the result was \#(descriptionString)"# +// 여는 따옴표 앞에 원하는 만큼 숫자 기호를 넣을 수 있습니다. +// 닫는 따옴표에서 일치시키기만 하면 됩니다. 또한 이스케이프 문자를 +// 백슬래시 뒤에 동일한 수의 숫자 기호로 변경합니다. + +let multiLineString = """ + This is a multi-line string. + It's called that because it takes up multiple lines (wow!) + Any indentation beyond the closing quotation marks is kept, the rest is discarded. + You can include " or "" in multi-line strings because the delimiter is three "s. + """ + +// 배열 +let shoppingList = ["catfish", "water", "tulips",] //마지막 요소 뒤에 쉼표를 허용합니다. +let secondElement = shoppingList[1] // 배열은 0부터 인덱싱됩니다. + +// let으로 선언된 배열은 불변입니다. 다음 줄은 컴파일 타임 오류를 발생시킵니다. +//shoppingList[2] = "mango" + +// 배열은 구조체이므로(나중에 자세히 설명), 동일한 객체를 참조하는 대신 복사본을 만듭니다. +var mutableShoppingList = shoppingList +mutableShoppingList[2] = "mango" + +// ==는 동등성입니다. +shoppingList == mutableShoppingList // false + +// let으로 선언된 딕셔너리도 불변입니다. +var occupations = [ + "Malcolm": "Captain", + "Kaylee": "Mechanic" +] +occupations["Jayne"] = "Public Relations" +// 딕셔너리도 구조체이므로 이것도 복사본을 만듭니다. +let immutableOccupations = occupations + +immutableOccupations == occupations // true + +// 배열과 딕셔너리는 모두 요소를 추가하면 자동으로 커집니다. +mutableShoppingList.append("blue paint") +occupations["Tim"] = "CEO" + +// 둘 다 비울 수 있습니다. +mutableShoppingList = [] +occupations = [:] + +let emptyArray = [String]() +let emptyArray2 = Array() // 위와 동일 +// [T]는 Array의 약어입니다. +let emptyArray3: [String] = [] // 타입을 명시적으로 선언하면 빈 배열로 설정할 수 있습니다. +let emptyArray4: Array = [] // 위와 동일 + +// [Key: Value]는 Dictionary의 약어입니다. +let emptyDictionary = [String: Double]() +let emptyDictionary2 = Dictionary() // 위와 동일 +var emptyMutableDictionary: [String: Double] = [:] +var explicitEmptyMutableDictionary: Dictionary = [:] // 위와 동일 + +// MARK: 기타 변수 +let øπΩ = "value" // 유니코드 변수 이름 +let 🤯 = "wow" // 이모티콘 변수 이름 + +// 키워드를 변수 이름으로 사용할 수 있습니다. +// 이것들은 지금 사용되지 않는 문맥 키워드이므로 허용됩니다. +let convenience = "keyword" +let weak = "another keyword" +let override = "another keyword" + +// 백틱을 사용하면 일반적으로 허용되지 않는 경우에도 키워드를 변수 이름으로 사용할 수 있습니다. +let `class` = "keyword" + +// MARK: - 옵셔널 + +/* + 옵셔널은 값이 있거나 값이 없음을 나타내는 nil(값 없음)을 + 포함하는 Swift 언어 기능입니다. + Nil은 다른 언어의 `null`과 거의 동일합니다. + 타입 뒤의 물음표(?)는 해당 타입의 옵셔널 값으로 표시합니다. + + 타입이 옵셔널이 아닌 경우 값이 보장됩니다. + + Swift는 모든 속성에 타입이 있어야 하므로 nil조차도 + 명시적으로 옵셔널 값으로 저장해야 합니다. + + Optional는 .none (nil)과 .some(T) (값) 케이스가 있는 열거형입니다. + */ + +var someOptionalString: String? = "optional" // nil일 수 있음 +// T?는 Optional의 약어입니다. — ?는 후위 연산자입니다 (구문 설탕) +let someOptionalString2: Optional = nil +let someOptionalString3 = String?.some("optional") // 첫 번째와 동일 +let someOptionalString4 = String?.none //nil + +/* + 값이 있는 옵셔널의 값에 액세스하려면 후위 연산자 !를 + 사용하여 강제 언래핑합니다. 강제 언래핑은 "이 옵셔널에 + 확실히 값이 있다는 것을 알고 있으니 주세요"라고 말하는 것과 같습니다. + + !를 사용하여 존재하지 않는 옵셔널 값에 액세스하려고 하면 + 런타임 오류가 발생합니다. !를 사용하여 값을 강제 언래핑하기 전에 + 항상 옵셔널에 nil이 아닌 값이 포함되어 있는지 확인하십시오. + */ + +if someOptionalString != nil { + // nil이 아님 + if someOptionalString!.hasPrefix("opt") { + print("has the prefix") + } +} + +// Swift는 "옵셔널 체이닝"을 지원합니다. 즉, 옵셔널 값의 함수를 +// 호출하거나 속성을 가져올 수 있으며 적절한 타입의 옵셔널이 됩니다. +// 이 작업을 여러 번 수행할 수 있으므로 "체이닝"이라는 이름이 붙었습니다. + +let empty = someOptionalString?.isEmpty // Bool? + +// if-let 구조 - +// if-let은 Swift의 특별한 구조로, 옵셔널 rhs에 +// 값이 있는지 확인하고, 있으면 언래핑하여 +// lhs에 할당할 수 있습니다. +if let someNonOptionalStringConstant = someOptionalString { + // `Some` 값이 있음, nil이 아님 + // someOptionalStringConstant는 String? 타입이 아닌 String 타입입니다. + if !someNonOptionalStringConstant.hasPrefix("ok") { + // 접두사가 없음 + } +} + +//if-var도 허용됩니다! +if var someNonOptionalString = someOptionalString { + someNonOptionalString = "Non optional AND mutable" + print(someNonOptionalString) +} + +// 하나의 if-let 문에서 여러 옵셔널 값을 바인딩할 수 있습니다. +// 바인딩된 값 중 하나라도 nil이면 if 문이 실행되지 않습니다. +if let first = someOptionalString, let second = someOptionalString2, + let third = someOptionalString3, let fourth = someOptionalString4 { + print("\(first), \(second), \(third), and \(fourth) are all not nil") +} + +//if-let은 ","(쉼표) 절을 지원하며, 이는 새로 바인딩된 +// 옵셔널 값에 대한 조건을 강제하는 데 사용할 수 있습니다. +// 할당과 "," 절 모두 통과해야 합니다. +let someNumber: Int? = 7 +if let num = someNumber, num > 3 { + print("num is not nil and is greater than 3") +} + +// 암시적으로 언래핑된 옵셔널 — 언래핑할 필요가 없는 옵셔널 값 +let unwrappedString: String! = "Value is expected." + +// 차이점은 다음과 같습니다. +let forcedString = someOptionalString! // 느낌표 필요 +let implicitString = unwrappedString // 느낌표 필요 없음 + +/* + 암시적으로 언래핑된 옵셔널은 사용될 때마다 자동으로 + 언래핑되도록 허용하는 것으로 생각할 수 있습니다. + 사용할 때마다 옵셔널 이름 뒤에 느낌표를 붙이는 대신, + 선언할 때 옵셔널 타입 뒤에 느낌표를 붙입니다. + */ + +// 그렇지 않으면 암시적으로 언래핑된 옵셔널을 일반 옵셔널과 동일하게 +// 취급할 수 있습니다 (예: if-let, != nil 등). + +// Swift 5 이전에는 T!가 ImplicitlyUnwrappedOptional의 약어였습니다. +// Swift 5 이상에서는 ImplicitlyUnwrappedOptional을 사용하면 컴파일 타임 오류가 발생합니다. +//var unwrappedString2: ImplicitlyUnwrappedOptional = "Value is expected." //오류 + +// nil 병합 연산자 ??는 옵셔널이 nil이 아닌 값을 포함하는 경우 언래핑하거나 기본값을 반환합니다. +someOptionalString = nil +let someString = someOptionalString ?? "abc" +print(someString) // abc +// a ?? b는 a != nil ? a! : b의 약어입니다. + +// MARK: - 제어 흐름 + +let condition = true +if condition { print("condition is true") } // 중괄호를 생략할 수 없음 + +if theAnswer > 50 { + print("theAnswer > 50") +} else if condition { + print("condition is true") +} else { + print("Neither are true") +} + +// `if` 문의 조건은 `Bool`이어야 하므로 다음 코드는 오류이며, 0과 암시적으로 비교되지 않습니다. +//if 5 { +// print("5 is not zero") +//} + +// Switch +// 철저해야 함 +// 암시적으로 fall through하지 않음, fallthrough 키워드 사용 +// 매우 강력함, `if` 문에 구문 설탕을 더한 것으로 생각 +// String, 객체 인스턴스 및 기본 타입(Int, Double 등) 지원 +let vegetable = "red pepper" +let vegetableComment: String +switch vegetable { +case "celery": + vegetableComment = "Add some raisins and make ants on a log." +case "cucumber", "watercress": // 여러 값 일치 + vegetableComment = "That would make a good tea sandwich." +case let localScopeValue where localScopeValue.hasSuffix("pepper"): + vegetableComment = "Is it a spicy \(localScopeValue)?" +default: // 필수 (모든 가능한 입력을 처리하기 위해) + vegetableComment = "Everything tastes good in soup." +} +print(vegetableComment) + +// `for-in` 루프를 사용하여 배열, 딕셔너리, 범위 등과 같은 시퀀스를 반복합니다. +for element in shoppingList { + print(element) // shoppingList는 `[String]` 타입이므로 element는 `String` 타입입니다. +} +//딕셔너리를 반복해도 특정 순서가 보장되지 않습니다. +for (person, job) in immutableOccupations { + print("\(person)'s job is \(job)") +} +for i in 1...5 { + print(i, terminator: " ") // "1 2 3 4 5" 출력 +} +for i in 0..<5 { + print(i, terminator: " ") // "0 1 2 3 4" 출력 +} +//for index in range는 C 스타일 for 루프를 대체할 수 있습니다. +// for (int i = 0; i < 10; i++) { +// //코드 +// } +//는 다음과 같습니다. +// for i in 0..<10 { +// //코드 +// } +//1보다 큰 단계로 이동하려면 stride(from:to:by:) 또는 stride(from:through:by) 함수를 사용합니다. +//`for i in stride(from: 0, to: 10, by: 2)`는 `for (int i = 0; i < 10; i += 2)`와 동일합니다. +//`for i in stride(from: 0, through: 10, by: 2)`는 `for (int i = 0; i <= 10; i += 2)`와 동일합니다. + +// while 루프는 대부분의 언어와 같습니다. +var i = 0 +while i < 5 { + i += Bool.random() ? 1 : 0 + print(i) +} + +// 이것은 다른 언어의 do-while 루프와 같습니다. — 루프의 본문은 최소 한 번 실행됩니다. +repeat { + i -= 1 + i += Int.random(in: 0...3) +} while i < 5 + +// continue 문은 다음 반복에서 루프를 계속 실행합니다. +// break 문은 루프를 즉시 종료합니다. + +// MARK: - 함수 + +// 함수는 일급 타입이므로 함수에 중첩될 수 있으며 전달될 수 있습니다. + +// Swift 헤더 문서가 있는 함수 (Swift 수정 마크다운 구문으로 서식 지정) + +/// 인사 작업입니다. +/// +/// - 매개변수: +/// - name: 이름입니다. +/// - day: 요일입니다. +/// - 반환값: 이름과 요일 값을 포함하는 문자열입니다. +func greet(name: String, day: String) -> String { + return "Hello \(name), today is \(day)." +} +greet(name: "Bob", day: "Tuesday") + +// 이상적으로는 함수 이름과 매개변수 레이블이 결합되어 문장과 유사한 함수 호출을 만듭니다. +func sayHello(to name: String, onDay day: String) -> String { + return "Hello \(name), the day is \(day)" +} +sayHello(to: "John", onDay: "Sunday") + +//아무것도 반환하지 않는 함수는 반환 화살표를 생략할 수 있습니다. Void를 반환한다고 말할 필요가 없습니다(물론 가능합니다). +func helloWorld() { + print("Hello, World!") +} + +// 인수 레이블은 비워 둘 수 있습니다. +func say(_ message: String) { + print(#"I say "\#(message)""#) +} +say("Hello") + +// 기본 매개변수는 함수를 호출할 때 생략할 수 있습니다. +func printParameters(requiredParameter r: Int, optionalParameter o: Int = 10) { + print("The required parameter was \(r) and the optional parameter was \(o)") +} +printParameters(requiredParameter: 3) +printParameters(requiredParameter: 3, optionalParameter: 6) + +// 가변 인자 — 함수당 한 세트만. +func setup(numbers: Int...) { + // 배열입니다. + let _ = numbers[0] + let _ = numbers.count +} + +// 참조에 의한 전달 +func swapTwoInts(a: inout Int, b: inout Int) { + let tempA = a + a = b + b = tempA +} +var someIntA = 7 +var someIntB = 3 +swapTwoInts(a: &someIntA, b: &someIntB) //변수 이름 앞에 &를 붙여 호출해야 합니다. +print(someIntB) // 7 + +type(of: greet) // (String, String) -> String +type(of: helloWorld) // () -> Void + +// 함수 전달 및 반환 +func makeIncrementer() -> ((Int) -> Int) { + func addOne(number: Int) -> Int { + return 1 + number + } + return addOne +} +var increment = makeIncrementer() +increment(7) + +func performFunction(_ function: (String, String) -> String, on string1: String, and string2: String) { + let result = function(string1, string2) + print("The result of calling the function on \(string1) and \(string2) was \(result)") +} + +// 튜플로 여러 항목을 반환하는 함수 +func getGasPrices() -> (Double, Double, Double) { + return (3.59, 3.69, 3.79) +} +let pricesTuple = getGasPrices() +let price = pricesTuple.2 // 3.79 +// _ (밑줄)을 사용하여 튜플(또는 다른) 값 무시 +let (_, price1, _) = pricesTuple // price1 == 3.69 +print(price1 == pricesTuple.1) // true +print("Gas price: \(price)") + +// 레이블/이름이 있는 튜플 매개변수 +func getGasPrices2() -> (lowestPrice: Double, highestPrice: Double, midPrice: Double) { + return (1.77, 37.70, 7.37) +} +let pricesTuple2 = getGasPrices2() +let price2 = pricesTuple2.lowestPrice +let (_, price3, _) = pricesTuple2 +print(pricesTuple2.highestPrice == pricesTuple2.1) // true +print("Highest gas price: \(pricesTuple2.highestPrice)") + +// guard 문 +func testGuard() { + // guard는 조기 종료 또는 중단을 제공하여 오류 처리 코드를 조건에 가깝게 배치합니다. + // 선언하는 변수를 guard 문과 동일한 범위에 배치합니다. + // "파멸의 피라미드"를 피하기 쉽게 만듭니다. + guard let aNumber = Optional(7) else { + return // guard 문은 반드시 있는 범위를 종료해야 합니다. + // 일반적으로 `return` 또는 `throw`를 사용합니다. + } + + print("number is \(aNumber)") +} +testGuard() + +// print 함수는 다음과 같이 선언됩니다. +// func print(_ input: Any..., separator: String = " ", terminator: String = "\n") +// 줄 바꿈 없이 출력하려면: +print("No newline", terminator: "") +print("!") + +// MARK: - 클로저 + +var numbers = [1, 2, 6] + +// 함수는 특수한 경우의 클로저({})입니다. + +// 클로저 예제. +// `->`는 인수와 반환 타입을 구분합니다. +// `in`은 클로저 헤더와 클로저 본문을 구분합니다. +numbers.map({ + (number: Int) -> Int in + let result = 3 * number + return result +}) + +// 위와 같이 타입이 알려진 경우 다음과 같이 할 수 있습니다. +numbers = numbers.map({ number in 3 * number }) +// 또는 이렇게도 할 수 있습니다. +//numbers = numbers.map({ $0 * 3 }) + +print(numbers) // [3, 6, 18] + +// 후행 클로저 +numbers = numbers.sorted { $0 > $1 } + +print(numbers) // [18, 6, 3] + +// MARK: - 열거형 + +// 열거형은 선택적으로 특정 타입이거나 자체적으로 있을 수 있습니다. +// 클래스처럼 메서드를 포함할 수 있습니다. + +enum Suit { + case spades, hearts, diamonds, clubs + var icon: Character { + switch self { + case .spades: + return "♤" + case .hearts: + return "♡" + case .diamonds: + return "♢" + case .clubs: + return "♧" + } + } +} + +// 열거형 값은 약식 구문을 허용하며, 변수가 명시적으로 +// 선언된 경우 열거형 타입을 입력할 필요가 없습니다. +var suitValue: Suit = .hearts + +// CaseIterable 프로토콜을 준수하면 모든 값을 포함하는 +// allCases 속성이 자동으로 합성됩니다. +// 연관된 값이나 @available 속성이 없는 열거형에서 작동합니다. +enum Rank: CaseIterable { + case ace + case two, three, four, five, six, seven, eight, nine, ten + case jack, queen, king + var icon: String { + switch self { + case .ace: + return "A" + case .two: + return "2" + case .three: + return "3" + case .four: + return "4" + case .five: + return "5" + case .six: + return "6" + case .seven: + return "7" + case .eight: + return "8" + case .nine: + return "9" + case .ten: + return "10" + case .jack: + return "J" + case .queen: + return "Q" + case .king: + return "K" + } + } +} + +for suit in [Suit.clubs, .diamonds, .hearts, .spades] { + for rank in Rank.allCases { + print("\(rank.icon)\(suit.icon)") + } +} + +// 문자열 열거형은 직접적인 원시 값 할당을 가질 수 있거나 +// 원시 값은 열거형 필드에서 파생됩니다. +enum BookName: String { + case john + case luke = "Luke" +} +print("Name: \(BookName.john.rawValue)") + +// 연관된 값이 있는 열거형 +enum Furniture { + // Int와 연관 + case desk(height: Int) + // String 및 Int와 연관 + case chair(String, Int) + + func description() -> String { + // let의 두 위치 모두 허용됩니다. + switch self { + case .desk(let height): + return "Desk with \(height) cm" + case let .chair(brand, height): + return "Chair of \(brand) with \(height) cm" + } + } +} + +var desk: Furniture = .desk(height: 80) +print(desk.description()) // "Desk with 80 cm" +var chair = Furniture.chair("Foo", 40) +print(chair.description()) // "Chair of Foo with 40 cm" + +// MARK: - 구조체 및 클래스 + +/* + Swift의 구조체와 클래스는 많은 공통점을 가지고 있습니다. 둘 다 다음을 할 수 있습니다. + - 값을 저장하기 위한 속성 정의 + - 기능을 제공하기 위한 메서드 정의 + - 첨자 구문을 사용하여 값에 액세스하기 위한 첨자 정의 + - 초기 상태를 설정하기 위한 초기화자 정의 + - 기본 구현을 넘어 기능을 확장하기 위해 확장 + - 특정 종류의 표준 기능을 제공하기 위해 프로토콜 준수 + + 클래스는 구조체에 없는 추가 기능을 가지고 있습니다. + - 상속을 통해 한 클래스가 다른 클래스의 특성을 상속할 수 있습니다. + - 타입 캐스팅을 통해 런타임에 클래스 인스턴스의 타입을 확인하고 해석할 수 있습니다. + - 소멸자를 통해 클래스 인스턴스가 할당한 모든 리소스를 해제할 수 있습니다. + - 참조 계산을 통해 클래스 인스턴스에 대한 참조가 둘 이상 있을 수 있습니다. + + 이러한 이유 중 하나로 클래스를 사용해야 하는 경우가 아니라면 구조체를 사용하십시오. + + 구조체는 값 타입이고 클래스는 참조 타입입니다. + */ + +// MARK: 구조체 + +struct NamesTable { + let names: [String] + + // 사용자 정의 첨자 + subscript(index: Int) -> String { + return names[index] + } +} + +// 구조체에는 자동 생성된(암시적) 지정된 "멤버별" 초기화자가 있습니다. +let namesTable = NamesTable(names: ["Me", "Them"]) +let name = namesTable[1] +print("Name is \(name)") // Name is Them + +// MARK: 클래스 + +class Shape { + func getArea() -> Int { + return 0 + } +} + +class Rect: Shape { + var sideLength: Int = 1 + + // 사용자 정의 getter 및 setter 속성 + var perimeter: Int { + get { + return 4 * sideLength + } + set { + // `newValue`는 setter에서 사용할 수 있는 암시적 변수입니다. + sideLength = newValue / 4 + } + } + + // 계산된 속성은 변경될 수 있으므로 `var`로 선언해야 합니다. + var smallestSideLength: Int { + return self.sideLength - 1 + } + + // 속성을 지연 로드합니다. + // subShape는 getter가 호출될 때까지 nil(초기화되지 않음)으로 유지됩니다. + lazy var subShape = Rect(sideLength: 4) + + // 사용자 정의 getter 및 setter가 필요하지 않지만 + // 속성을 가져오거나 설정하기 전후에 코드를 실행하려면 + // `willSet` 및 `didSet`을 사용할 수 있습니다. + var identifier: String = "defaultID" { + // `someIdentifier` 인수는 새 값의 변수 이름이 됩니다. + willSet(someIdentifier) { + print(someIdentifier) + } + } + + init(sideLength: Int) { + self.sideLength = sideLength + // 사용자 정의 속성을 초기화할 때 항상 super.init을 마지막에 호출합니다. + super.init() + } + + func shrink() { + if sideLength > 0 { + sideLength -= 1 + } + } + + override func getArea() -> Int { + return sideLength * sideLength + } +} + +// 간단한 클래스 `Square`는 `Rect`를 확장합니다. +class Square: Rect { + // 편의 초기화자를 사용하여 지정된 초기화자를 더 빠르고 "편리하게" 호출합니다. + // 편의 초기화자는 동일한 클래스의 다른 초기화자를 호출하고 하나 이상의 매개변수에 기본값을 전달합니다. + // 편의 초기화자도 매개변수를 가질 수 있으며, 이는 호출된 초기화자 매개변수를 사용자 정의하거나 전달된 값에 따라 적절한 초기화자를 선택하는 데 유용합니다. + convenience init() { + self.init(sideLength: 5) + } +} + +var mySquare = Square() +print(mySquare.getArea()) // 25 +mySquare.shrink() +print(mySquare.sideLength) // 4 + +// 인스턴스 캐스팅 +let aShape = mySquare as Shape + +// 인스턴스 다운캐스팅: +// 다운캐스팅은 실패할 수 있으므로 결과는 옵셔널(as?) 또는 암시적으로 언래핑된 옵셔널(as!)일 수 있습니다. +let anOptionalSquare = aShape as? Square // aShape가 Square가 아니면 nil을 반환합니다. +let aSquare = aShape as! Square // aShape가 Square가 아니면 런타임 오류가 발생합니다. + +// 객체를 비교하는 ==와 달리 인스턴스를 비교합니다. +if mySquare === mySquare { + print("Yep, it's mySquare") +} + +// 옵셔널 초기화 +class Circle: Shape { + var radius: Int + override func getArea() -> Int { + return 3 * radius * radius + } + + // `init` 뒤에 물음표 접미사는 옵셔널 초기화입니다. + // nil을 반환할 수 있습니다. + init?(radius: Int) { + self.radius = radius + super.init() + + if radius <= 0 { + return nil + } + } +} + +var myCircle = Circle(radius: 1) +print(myCircle?.getArea()) // Optional(3) +print(myCircle!.getArea()) // 3 +var myEmptyCircle = Circle(radius: -1) +print(myEmptyCircle?.getArea()) // "nil" +if let circle = myEmptyCircle { + // myEmptyCircle이 nil이므로 실행되지 않습니다. + print("circle is not nil") +} + +// MARK: - 프로토콜 + +// 프로토콜은 다른 언어의 인터페이스라고도 합니다. + +// `protocol`은 준수하는 타입이 특정 +// 인스턴스 속성, 인스턴스 메서드, 타입 메서드, +// 연산자 및 첨자를 갖도록 요구할 수 있습니다. + +protocol ShapeGenerator { + var enabled: Bool { get set } + func buildShape() -> Shape +} + +// MARK: - 기타 + +// MARK: 타입 별칭 + +// 타입 별칭을 사용하면 한 타입(또는 타입의 구성)을 다른 이름으로 참조할 수 있습니다. +typealias Integer = Int +let myInteger: Integer = 0 + +// MARK: = 연산자 + +// 할당은 값을 반환하지 않습니다. 즉, 조건문에서 사용할 수 없으며, +// 다음 문장도 불법입니다. +// let multipleAssignment = theQuestion = "No questions asked" +//하지만 이렇게 할 수 있습니다. +let multipleAssignment = "No questions asked", secondConstant = "No answers given" + +// MARK: 범위 + +// ..< 및 ... 연산자는 범위를 생성합니다. + +// ...는 양쪽 끝을 포함합니다("닫힌 범위") — 수학적으로 [0, 10] +let _0to10 = 0...10 +// ..<는 왼쪽은 포함하고 오른쪽은 제외합니다("범위") — 수학적으로 [0, 10) +let singleDigitNumbers = 0..<10 +// 한쪽 끝을 생략할 수 있습니다("PartialRangeFrom") — 수학적으로 [0, ∞) +let toInfinityAndBeyond = 0... +// 또는 다른 쪽 끝("PartialRangeTo") — 수학적으로 (-∞, 0) +let negativeInfinityToZero = ..<0 +// ("PartialRangeThrough") — 수학적으로 (-∞, 0] +let negativeInfinityThroughZero = ...0 + +// MARK: 와일드카드 연산자 + +// Swift에서 _(밑줄)은 와일드카드 연산자로, 값을 무시할 수 있습니다. + +// 인수 레이블 없이 함수를 선언할 수 있습니다. +func function(_ labelLessParameter: Int, label labeledParameter: Int, labelAndParameterName: Int) { + print(labelLessParameter, labeledParameter, labelAndParameterName) +} +function(0, label: 0, labelAndParameterName: 0) + +// 함수의 반환 값을 무시할 수 있습니다. +func printAndReturn(_ str: String) -> String { + print(str) + return str +} +let _ = printAndReturn("Some String") + +// 튜플의 일부를 무시하고 일부를 유지할 수 있습니다. +func returnsTuple() -> (Int, Int) { + return (1, 2) +} +let (_, two) = returnsTuple() + +// 클로저 매개변수를 무시할 수 있습니다. +let closure: (Int, Int) -> String = { someInt, _ in + return "\(someInt)" +} +closure(1, 2) // 1 반환 + +// for 루프에서 값을 무시할 수 있습니다. +for _ in 0..<10 { + // 10번 실행할 코드 +} + +// MARK: 접근 제어 + +/* + Swift에는 5가지 수준의 접근 제어가 있습니다. + - Open: 가져오는 모든 모듈에서 액세스 가능하고 하위 클래스화 가능합니다. + - Public: 가져오는 모든 모듈에서 액세스 가능하며, 선언된 모듈에서 하위 클래스화 가능합니다. + - Internal: 선언된 모듈에서 액세스 가능하고 하위 클래스화 가능합니다. + - Fileprivate: 선언된 파일에서 액세스 가능하고 하위 클래스화 가능합니다. + - Private: 둘러싸는 선언에서 액세스 가능하고 하위 클래스화 가능합니다(내부 클래스/구조체/열거형 생각). + + 자세한 내용은 여기를 참조하십시오: https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html + */ + +// MARK: 재정의 방지 + +// 클래스나 인스턴스 메서드, 또는 속성 앞에 `final` 키워드를 추가하여 재정의를 방지할 수 있습니다. +class Shape { + final var finalInteger = 10 +} + +// 클래스가 하위 클래스화되는 것을 방지 +final class ViewManager { +} + +// MARK: 조건부 컴파일, 컴파일 타임 진단 및 가용성 조건 + +// 조건부 컴파일 +#if false +print("This code will not be compiled") +#else +print("This code will be compiled") +#endif +/* + 옵션은 다음과 같습니다. + os() macOS, iOS, watchOS, tvOS, Linux + arch() i386, x86_64, arm, arm64 + swift() >= 또는 < 뒤에 버전 번호 + compiler() >= 또는 < 뒤에 버전 번호 + canImport() 모듈 이름 + targetEnvironment() simulator + */ +#if swift(<3) +println() +#endif + +// 컴파일 타임 진단 +// #warning(message) 및 #error(message)를 사용하여 컴파일러가 경고 및/또는 오류를 내도록 할 수 있습니다. +#warning("This will be a compile-time warning") +// #error("This would be a compile-time error") + +//가용성 조건 +if #available(iOSMac 10.15, *) { + // macOS 10.15를 사용할 수 있으므로 여기에서 사용할 수 있습니다. +} else { + // macOS 10.15를 사용할 수 없으므로 대체 API를 사용합니다. +} + +// MARK: Any 및 AnyObject + +// Swift는 모든 타입의 값을 저장하는 것을 지원합니다. +// 이를 위해 `Any`와 `AnyObject`라는 두 가지 키워드가 있습니다. +// `AnyObject` == Objective-C의 `id` +// `Any`는 모든 값(클래스, Int, 구조체 등)과 함께 작동합니다. +var anyVar: Any = 7 +anyVar = "Changed value to a string, not good practice, but possible." +let anyObjectVar: AnyObject = Int(1) as NSNumber + +// MARK: 확장 + +// 확장을 사용하면 이미 선언된 타입에 추가 기능을 추가할 수 있으며, 소스 코드가 없는 타입에도 추가할 수 있습니다. + +// Square는 이제 `CustomStringConvertible` 프로토콜을 "준수"합니다. +extension Square: CustomStringConvertible { + var description: String { + return "Area: \(self.getArea()) - ID: \(self.identifier)" + } +} + +print("Square: \(mySquare)") + +// 내장 타입을 확장할 수도 있습니다. +extension Int { + var doubled: Int { + return self * 2 + } + + func multipliedBy(num: Int) -> Int { + return num * self + } + + mutating func multiplyBy(num: Int) { + self *= num + } +} + +print(7.doubled) // 14 +print(7.doubled.multipliedBy(num: 3)) // 42 + +// MARK: 제네릭 + +// 제네릭: Java 및 C#과 유사합니다. `where` 키워드를 사용하여 +// 제네릭의 요구 사항을 지정합니다. + +func findIndex(array: [T], valueToFind: T) -> Int? { + for (index, value) in array.enumerated() { + if value == valueToFind { + return index + } + } + return nil +} +findIndex(array: [1, 2, 3, 4], valueToFind: 3) // Optional(2) + +// 제네릭으로 타입을 확장할 수도 있습니다. +extension Array where Array.Element == Int { + var sum: Int { + var total = 0 + for el in self { + total += el + } + return total + } +} + +// MARK: 연산자 + +// 사용자 정의 연산자는 다음 문자로 시작할 수 있습니다. +// / = - + * % < > ! & | ^ . ~ +// 또는 +// 유니코드 수학, 기호, 화살표, 딩뱃 및 선/상자 그리기 문자. +prefix operator !!! + +// 사용 시 측면 길이를 세 배로 늘리는 접두사 연산자 +prefix func !!! (shape: inout Square) -> Square { + shape.sideLength *= 3 + return shape +} + +// 현재 값 +print(mySquare.sideLength) // 4 + +// 사용자 정의 !!! 연산자를 사용하여 측면 길이 변경, 크기를 3배 증가 +!!!mySquare +print(mySquare.sideLength) // 12 + +// 연산자는 제네릭일 수도 있습니다. +infix operator <-> +func <-> (a: inout T, b: inout T) { + let c = a + a = b + b = c +} + +var foo: Float = 10 +var bar: Float = 20 + +foo <-> bar +print("foo is \(foo), bar is \(bar)") // "foo is 20.0, bar is 10.0" + +// MARK: - 오류 처리 + +// `Error` 프로토콜은 오류를 throw하고 catch할 때 사용됩니다. +enum MyError: Error { + case badValue(msg: String) + case reallyBadValue(msg: String) +} + +// `throws`로 표시된 함수는 `try`를 사용하여 호출해야 합니다. +func fakeFetch(value: Int) throws -> String { + guard 7 == value else { + throw MyError.reallyBadValue(msg: "Some really bad value") + } + + return "test" +} + +func testTryStuff() { + // 오류가 발생하지 않을 것으로 가정하고, 그렇지 않으면 런타임 예외가 발생합니다. + let _ = try! fakeFetch(value: 7) + + // 오류가 발생하면 계속 진행하지만, 값이 nil이면 + // 이미 옵셔널인 경우에도 모든 반환 값을 옵셔널로 래핑합니다. + let _ = try? fakeFetch(value: 7) + + do { + // `catch` 블록을 통해 오류 처리를 제공하는 일반적인 try 작업 + try fakeFetch(value: 1) + } catch MyError.badValue(let msg) { + print("Error message: \(msg)") + } catch { + // 철저해야 함 + } +} +testTryStuff() +``` diff --git a/ko/tailspin.md b/ko/tailspin.md new file mode 100644 index 0000000000..5ee5eeaaaf --- /dev/null +++ b/ko/tailspin.md @@ -0,0 +1,383 @@ +--- +name: Tailspin +filename: learntailspin.tt +contributors: + - ["Torbjörn Gannholm", "https://github.com/tobega/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +**Tailspin**은 파이프라인에서 값의 스트림으로 작동합니다. 종종 +프로그램이 기계이고 입력 데이터가 프로그램이라고 느낄 수 있습니다. + +Tailspin이 주류가 되거나 프로덕션 준비가 될 가능성은 낮지만, +좋은 방향으로 프로그래밍에 대한 생각을 바꿀 것입니다. + +```c +// 줄 끝까지 주석 + +// ->로 구분된 단계가 있는 파이프라인에서 데이터 처리 +// 문자열 리터럴은 작은따옴표로 구분됩니다. +// 느낌표(!)는 싱크 또는 파이프의 끝을 나타냅니다. +// OUT은 표준 출력 객체이고, ::write는 출력을 쓰는 메시지입니다. +'Hello, World!' -> !OUT::write + +// 문자열에 입력하여 줄 바꿈 출력 (여러 줄 문자열) +' +' -> !OUT::write +// 또는 $#와 ; 사이에 줄 바꿈(10)에 대한 10진수 유니코드 값 출력 +'$#10;' -> !OUT::write + +// 불변의 명명된 값 정의. 값 구문은 매우 리터럴합니다. +def names: ['Adam', 'George', 'Jenny', 'Lucy']; + +// 각 이름을 처리하기 위해 목록 스트리밍. $를 사용하여 값을 가져오는 것에 유의하십시오. +// 파이프라인의 현재 값은 항상 $입니다. +// 문자열 보간은 $로 시작하여 ;로 끝납니다. +$names... -> 'Hello $;! +' -> !OUT::write + +// 보간에 스트리밍하고 보간을 중첩할 수도 있습니다. +// 괄호를 사용한 목록 인덱싱 및 슬라이스 추출에 유의하십시오. +// ~를 사용하여 범위에 대한 배타적 경계를 나타내는 것에 유의하십시오. +// 'Hello Adam, George, Jenny and Lucy!'를 출력합니다. +'Hello $names(first);$names(first~..~last)... -> ', $;'; and $names(last);! +' -> !OUT::write + +// 다른 사람에게 다른 말을 조건부로 합니다. +// 매처(조건식)는 꺾쇠괄호로 구분됩니다. +// 위에서 아래로 평가되는 매처 집합은 템플릿(함수)에 있어야 합니다. +// 여기서는 \(에서 \)로 구분된 인라인 템플릿입니다. +// 리터럴 ' 및 $를 얻기 위해 이중 '' 및 $$를 사용하는 것에 유의하십시오. +$names... -> \( + when <='Adam'> do 'What''s up $;?' ! + when <='George'> do 'George, where are the $$10 you owe me?' ! + otherwise 'Hello $;!' ! +\) -> '$;$#10;' -> !OUT::write + +// 템플릿(함수)을 정의할 수도 있습니다. +// 단독 !는 제어를 반환하지 않고 호출 파이프라인으로 값을 내보냅니다. +// #은 매처에 의해 일치될 값을 보냅니다. +// 템플릿은 항상 하나의 입력 값을 받고 0개 이상의 출력을 내보냅니다. +templates collatz-sequence + when <..0> do 'The start seed must be a positive integer' ! + when <=1> do $! +// ?(에서 )까지는 계산된 값을 일치시킬 수 있습니다. "and"로 연결할 수 있습니다. + when )> do + $ ! + 3 * $ + 1 -> # + otherwise + $ ! + $ ~/ 2 -> # +end collatz-sequence + +// 한 줄에 공백으로 구분된 임의의 시작점에서 콜라츠 시퀀스 +1000 -> SYS::randomInt -> $ + 1 -> collatz-sequence -> '$; ' -> !OUT::write +' +' -> !OUT::write + +// 인덱싱된 목록 템플릿으로 한 줄에 10개씩 서식이 지정된 콜라츠 시퀀스 +// 대괄호는 묶인 파이프라인 결과의 목록을 생성합니다. +// \[i](에서 \)까지는 목록의 각 값에 적용할 템플릿을 정의하며, +// i(또는 선택한 식별자)는 인덱스를 보유합니다. +[1000 -> SYS::randomInt -> $ + 1 -> collatz-sequence] +-> \[i]( + when <=1|?($i mod 10 <=0>)> do '$;$#10;' ! + otherwise '$; ' ! +\)... -> !OUT::write + +// 범위에는 선택적 보폭이 있을 수 있습니다. +def odd-numbers: [1..100:2]; + +// 로컬에서 가변 상태 사용. 템플릿당 하나의 변수, 항상 @라고 함 +templates product + @: $(first); + $(first~..last)... -> @: $@ * $; + $@ ! +end product + +$odd-numbers(6..8) -> product -> !OUT::write +' +' -> !OUT::write + +// 프로세서 객체를 사용하여 가변 상태 유지. +// 외부 @는 내부 컨텍스트에서 이름으로 참조해야 합니다. +// 싱크 템플릿은 출력이 없으며 접두사 !로 호출됩니다. +// 소스 템플릿은 입력이 없으며 접두사 $로 호출됩니다. +processor Product + @: 1; + sink accumulate + @Product: $@Product * $; + end accumulate + source result + $@Product ! + end result +end Product + +// 프로세서는 생성자 템플릿입니다. 이것은 $(입력 없음)으로 호출됩니다. +def multiplier: $Product; + +// ::로 메시지를 보내 객체 템플릿 호출 +1..7 -> !multiplier::accumulate +-1 -> !multiplier::accumulate +$multiplier::result -> 'The product is $; +' -> !OUT::write + +// 수집기 인터페이스를 구현하는 프로세서에 대한 구문 설탕 +1..7 -> ..=Product -> 'The collected product is $;$#10;' -> !OUT::write + +// 유한한 값 집합에 대해 심볼 집합(본질적으로 열거형)을 정의할 수 있습니다. +data colour #{green, red, blue, yellow} + +// 프로세서 타입 상태를 사용하여 상태를 깔끔하게 모델링합니다. +// 마지막으로 명명된 가변 상태 값 집합이 타입 상태를 결정합니다. +processor Lamp + def colours: $; + @Off: 0; + state Off + source switchOn + @On: $@Off mod $colours::length + 1; + 'Shining a $colours($@On); light$#10;' ! + end switchOn + end Off + state On + source turnOff + @Off: $@On; + 'Lamp is off$#10;' ! + end turnOff + end On +end Lamp + +def myLamp: [colour#green, colour#blue] -> Lamp; + +$myLamp::switchOn -> !OUT::write // Shining a green light +$myLamp::turnOff -> !OUT::write // Lamp is off +$myLamp::switchOn -> !OUT::write // Shining a blue light +$myLamp::turnOff -> !OUT::write // Lamp is off +$myLamp::switchOn -> !OUT::write // Shining a green light + +// 정규식을 사용하여 문자열 테스트 +['banana', 'apple', 'pear', 'cherry']... -> \( + when <'.*a.*'> do '$; contains an ''a''' ! + otherwise '$; has no ''a''' ! +\) -> '$; +' -> !OUT::write + +// 정규식과 정의된 규칙으로 작곡가를 사용하여 문자열 구문 분석 +composer parse-stock-line + {inventory-id: (), name: <'\w+'> (), currency: <'.{3}'>, + unit-price: (?) ?} + rule parts: associated-parts: [+] + rule part: <'[A-Z]\d+'> (<=','>?) +end parse-stock-line + +'705 gizmo EUR5 A67,G456,B32' -> parse-stock-line -> !OUT::write +// {associated-parts: [A67, G456, B32], currency: EUR, +// inventory-id: 705, name: gizmo, unit-price: 5} +' +' -> !OUT::write + +// 문자열을 스트리밍하여 글리프로 분할합니다. +// 목록은 인덱스 배열로 인덱싱/슬라이스할 수 있습니다. +// ['h','e','l','l','o']를 출력하고, 배열/목록 인덱싱은 1부터 시작합니다. +['abcdefghijklmnopqrstuvwxyz'...] -> $([8,5,12,12,15]) -> !OUT::write +' +' -> !OUT::write + +// 위에서는 원시 문자열만 사용했습니다. +// 문자열은 태그로 결정되는 다른 타입을 가질 수 있습니다. +// 더 넓은 타입 경계가 설정되지 않은 한 다른 타입을 비교하는 것은 오류입니다. +// 타입 경계는 ´´로 주어지며 ''는 태그가 있거나 원시인 모든 문자열 값을 의미합니다. +templates get-string-type + when <´''´ '.*'> do '$; is a raw string' ! + when <´''´ id´'\d+'> do '$; is a numeric id string' ! + when <´''´ =id´'foo'> do 'id foo found' ! + when <´''´ id´'.*'> do '$; is an id' ! + when <´''´ name´'.+'> do '$; is a name' ! + otherwise '$; is not a name or id, nor a raw string' ! +end get-string-type + +[name´'Anna', 'foo', id´'789', city´'London', id´'xzgh', id´'foo']... +-> get-string-type -> '$; +' -> !OUT::write + +// 숫자는 원시, 태그 또는 측정 단위를 가질 수 있습니다. +// 타입 ..은 태그, 측정 또는 원시인 모든 숫자 값입니다. +templates get-number-type + when <´..´ =inventory-id´86> do 'inventory-id 86 found' ! + when <´..´ inventory-id´100..> do '$; is an inventory-id >= 100' ! + when <´..´ inventory-id´0..|..inventory-id´0> do '$; is an inventory-id' ! + when <´..´ 0"m"..> do '$; is an m-measure >= 0"m"' ! + when <´..´ ..0|0..> do '$; is a raw number' ! + otherwise '$; is not a positive m-measure nor an inventory-id, nor raw' ! +end get-number-type + +[inventory-id´86, inventory-id´6, 78"m", 5"s", 99, inventory-id´654]... +-> get-number-type -> '$; +' -> !OUT::write + +// 측정 단위는 산술에 사용할 수 있으며, "1"은 스칼라 단위입니다. +// 측정 단위를 혼합할 때는 결과 측정 단위로 캐스팅해야 합니다. +4"m" + 6"m" * 3"1" -> ($ ~/ 2"s")"m/s" -> '$; +' -> !OUT::write + +// 태그가 있는 식별자는 산술에 사용될 때 원시 숫자로 만들어야 합니다. +// 그런 다음 원하는 경우 결과를 태그가 있는 식별자로 다시 캐스팅할 수 있습니다. +inventory-id´300 -> inventory-id´($::raw + 1) -> get-number-type -> '$; +' -> !OUT::write + +// 필드는 기본적으로 원시 문자열 또는 숫자를 태그하여 자동으로 타입을 지정합니다. +// 필드에 잘못된 타입을 할당할 수 없습니다. +def item: { inventory-id: 23, name: 'thingy', length: 12"m" }; + +'Field inventory-id $item.inventory-id -> get-number-type; +' -> !OUT::write +'Field name $item.name -> get-string-type; +' -> !OUT::write +'Field length $item.length -> get-number-type; +' -> !OUT::write + +// 타입을 정의하고 타입 테스트로 사용할 수 있습니다. 이것은 또한 필드를 정의합니다. +// 표준 플레이트 필드에 비표준 플레이트를 할당하는 것은 오류입니다. +data standard-plate <'[A-Z]{3}[0-9]{3}'> + +[['Audi', 'XYZ345'], ['BMW', 'I O U']]... -> \( + when )> do {make: $(1), standard-plate: $(2)}! + otherwise {make: $(1), vanity-plate: $(2)}! +\) -> '$; +' -> !OUT::write + +// 유니온 타입을 정의할 수 있습니다. +data age <"years"|"months"> + +[ {name: 'Cesar', age: 20"years"}, + {name: 'Francesca', age: 19"years"}, + {name: 'Bobby', age: 11"months"}]... +-> \( +// 구조에 대한 조건부 테스트는 필드 테스트와 함께 리터럴처럼 보입니다. + when <{age: <13"years"..19"years">}> do '$.name; is a teenager'! + when <{age: <"months">}> do '$.name; is a baby'! +// 모든 경우를 처리할 필요는 없습니다. 'Cesar'는 그냥 무시됩니다. +\) -> '$; +' -> !OUT::write + +// 배열/목록 인덱스는 기본적으로 1부터 시작하지만 선택할 수 있습니다. +// 슬라이스는 실제 배열과 겹치는 부분을 반환합니다. +[1..5] -> $(-2..2) -> '$; +' -> !OUT::write // [1,2] 출력 +0:[1..5] -> $(-2..2) -> '$; +' -> !OUT::write // [1,2,3] 출력 +-2:[1..5] -> $(-2..2) -> '$; +' -> !OUT::write // [1,2,3,4,5] 출력 + +// 배열은 측정 단위 또는 태그가 있는 식별자의 인덱스를 가질 수 있습니다. +def game-map: 0"y":[ + 1..5 -> 0"x":[ + 1..5 -> level´1:[ + 1..3 -> { + level: $, + terrain-id: 6 -> SYS::randomInt, + altitude: (10 -> SYS::randomInt)"m" + } + ] + ] +]; + +// 프로젝션(인덱싱)은 여러 차원에 걸쳐 있을 수 있습니다. +$game-map(3"y"; 1"x"..3"x"; level´1; altitude:) -> '$; +' -> !OUT::write // 세 개의 고도 값 목록을 제공합니다. + +// 통계를 얻기 위해 평탄화하고 그룹화 프로젝션을 수행합니다. +// Count 및 Max는 내장된 수집기 프로세서입니다. +[$game-map... ... ...] -> $(collect { + occurences: Count, + highest-on-level: Max&{by: :(altitude:), select: :(level:)} + } by $({terrain-id:})) +-> !OUT::write +' +' -> !OUT::write + +// 관계는 구조/레코드의 집합입니다. +// 여기서는 모든 고유한 {level:, terrain-id:, altitude:} 조합을 얻습니다. +def location-types: {|$game-map... ... ...|}; + +// 프로젝션은 구조를 다시 매핑할 수 있습니다. §는 상대 접근자입니다. +$location-types({terrain-id:, foo: §.level::raw * §.altitude}) +-> '$; +' -> !OUT::write + +// 관계형 대수 연산자는 관계에 사용할 수 있습니다. +($location-types join {| {altitude: 3"m"} |}) +-> !OUT::write +' +' -> !OUT::write + +// 이항 연산에 대한 자체 연산자 정의 +operator (left dot right) + $left -> \[i]($ * $right($i)!\)... -> ..=Sum&{of: :()} ! +end dot + +([1,2,3] dot [2,5,8]) -> 'dot product: $; +' -> !OUT::write + +// 템플릿 동작을 변경하기 위해 매개변수 제공 +templates die-rolls&{sides:} + 1..$ -> $sides::raw -> SYS::randomInt -> $ + 1 ! +end die-rolls + +[5 -> die-rolls&{sides: 4}] -> '$; +' -> !OUT::write + +// 템플릿을 매개변수로 전달하고, 일부 매개변수를 미리 채울 수 있습니다. +source damage-roll&{first:, second:, third:} + (1 -> first) + (1 -> second) + (1 -> third) ! +end damage-roll + +$damage-roll&{first: die-rolls&{sides:4}, + second: die-rolls&{sides:6}, third: die-rolls&{sides:20}} +-> 'Damage done is $; +' -> !OUT::write + +// 인라인으로 테스트 작성. 명령줄에서 --test 플래그로 실행 +// 매처의 ~는 "not"을 의미하며, +// 배열 내용 매처는 < 1 및 > 4인 요소를 일치시킵니다. +test 'die-rolls' + assert [100 -> die-rolls&{sides: 4}] <~[<..~1|4~..>]> 'all rolls 1..4' +end 'die-rolls' + +// 테스트에 수정된 모듈 제공 (테스트 더블 또는 모의 객체라고도 함) +// IN은 표준 입력 객체이고 ::lines는 모든 줄을 가져옵니다. +source read-numbers + $IN::lines -> # + when <'\d+'> do $! +end read-numbers + +test 'read numbers from input' + use shadowed core-system/ + processor MockIn + source lines + [ + '12a', + '65', + 'abc' + ]... ! + end lines + end MockIn + def IN: $MockIn; + end core-system/ + assert $read-numbers <=65> 'Only 65 is read' +end 'read numbers from input' + +// 바이트 배열로 작업할 수 있습니다. +composer hexToBytes + +end hexToBytes + +'1a5c678d' -> hexToBytes -> ($ and [x 07 x]) -> $(last-1..last) -> '$; +' -> !OUT::write // 0005 출력 +``` + +## 더 읽을거리 + +- [Tailspin 메인 사이트](https://github.com/tobega/tailspin-v0/) +- [Tailspin 언어 참조](https://github.com/tobega/tailspin-v0/blob/master/TailspinReference.md) diff --git a/ko/tcl.md b/ko/tcl.md new file mode 100644 index 0000000000..3d231aad1a --- /dev/null +++ b/ko/tcl.md @@ -0,0 +1,498 @@ +--- +name: Tcl +contributors: + - ["Poor Yorick", "https://pooryorick.com/"] +filename: learntcl.tcl +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Tcl은 [John Ousterhout](https://wiki.tcl-lang.org/page/John+Ousterhout)이 저술한 회로 설계 도구를 위한 재사용 가능한 스크립팅 언어로 만들어졌습니다. 1997년에 그는 Tcl로 [ACM 소프트웨어 시스템 상](https://en.wikipedia.org/wiki/ACM_Software_System_Award)을 수상했습니다. Tcl은 내장형 스크립팅 언어와 일반 프로그래밍 언어로 모두 사용할 수 있습니다. 동적 문자열, 목록 및 해시 테이블과 같은 데이터 구조를 제공하므로 스크립팅 기능이 필요하지 않은 경우에도 이식 가능한 C 라이브러리로 사용할 수 있습니다. C 라이브러리는 또한 동적 라이브러리 로드, 문자열 서식 지정 및 코드 변환, 파일 시스템 작업, 네트워크 작업 등을 위한 이식 가능한 기능을 제공합니다. Tcl의 다양한 기능은 다음과 같습니다. + +* 편리한 크로스 플랫폼 네트워킹 API + +* 완전 가상화된 파일 시스템 + +* 스택 가능한 I/O 채널 + +* 핵심적으로 비동기 + +* 전체 코루틴 + +* 견고하고 사용하기 쉬운 것으로 알려진 스레딩 모델 + + +Tcl은 Lisp와 많은 공통점을 가지고 있지만, 목록 대신 Tcl은 문자열을 언어의 통화로 사용합니다. 모든 값은 문자열입니다. 목록은 정의된 형식을 가진 문자열이며, 프로시저(스크립트)의 본문도 블록이 아닌 문자열입니다. 성능을 달성하기 위해 Tcl은 내부적으로 이러한 값의 구조화된 표현을 캐시합니다. 예를 들어, 목록 루틴은 내부 캐시된 표현에서 작동하며, Tcl은 스크립트에서 실제로 필요한 경우 문자열 표현을 업데이트합니다. Tcl의 쓰기 시 복사 디자인을 통해 스크립트 작성자는 실제로 추가 메모리 오버헤드 없이 큰 데이터 값을 전달할 수 있습니다. 프로시저는 "uplevel", "upvar" 및 "trace"와 같은 더 동적인 루틴을 사용하지 않는 한 자동으로 바이트 컴파일됩니다. + +Tcl은 프로그래밍하기에 즐겁습니다. Lisp, Forth 또는 Smalltalk가 흥미롭다고 생각하는 해커 유형뿐만 아니라 자신의 의지에 따라 구부러지는 도구로 사업을 시작하려는 엔지니어와 과학자에게도 어필할 것입니다. 루핑 및 수학 연산과 같이 일반적으로 다른 언어의 구문에 내장된 것을 포함하여 모든 프로그래밍 기능을 루틴으로 노출하는 원칙은 프로젝트에 필요한 모든 도메인별 기능의 배경으로 사라지게 합니다. Lisp보다 훨씬 가벼운 구문은 그냥 방해가 되지 않습니다. + + + +```tcl +#! /bin/env tclsh + +############################################################################### +## 1. 지침 +############################################################################### + +# Tcl은 Sh나 C가 아닙니다! 표준 셸 인용 습관이 Tcl에서 거의 작동하고 사람들이 Tcl을 익히고 다른 언어에서 아는 구문으로 버티려고 하기 때문에 이 말을 해야 합니다. 처음에는 작동하지만 스크립트가 더 복잡해지면 곧 좌절로 이어집니다. + +# 중괄호는 인용 메커니즘이며 코드 블록이나 목록을 구성하는 구문이 아닙니다. Tcl에는 둘 다 없습니다. 중괄호는 특수 문자를 이스케이프하는 데 사용되므로 프로시저 본문과 목록으로 해석되어야 하는 문자열을 인용하는 데 적합합니다. + + +############################################################################### +## 2. 구문 +############################################################################### + +# 스크립트는 개행 문자나 세미콜론으로 구분된 명령으로 구성됩니다. 각 명령은 루틴에 대한 호출입니다. 첫 번째 단어는 호출할 루틴의 이름이고, 후속 단어는 루틴에 대한 인수입니다. 단어는 공백으로 구분됩니다. 각 인수는 명령의 단어이므로 이미 문자열이며 인용되지 않을 수 있습니다. +set part1 Sal +set part2 ut; set part3 ations + + +# 달러 기호는 변수 대입을 도입합니다. +set greeting $part1$part2$part3 + + +# "set"에 변수 이름만 주어지면 해당 변수의 값을 반환합니다. +set part3 ;# 변수의 값을 반환합니다. + + +# 왼쪽 및 오른쪽 대괄호는 단어에 대입할 결과를 위해 평가할 스크립트를 포함합니다. +set greeting $part1$part2[set part3] + + +# 포함된 스크립트는 여러 명령으로 구성될 수 있으며, 마지막 명령은 대입 결과를 제공합니다. +set greeting $greeting[ + incr i + incr i + incr i +] +puts $greeting ;# 출력은 "Salutations3"입니다. + +# 명령의 모든 단어는 루틴 이름을 포함하여 문자열이므로 대입을 사용할 수 있습니다. 이 변수 할당을 감안할 때, +set action pu + +# 다음 세 가지 명령은 동일합니다. +puts $greeting +${action}ts $greeting +[set action]ts $greeting + + +# 백슬래시는 문자의 특수 의미를 억제합니다. +set amount \$16.42 + + +# 백슬래시는 특정 문자에 특수 의미를 추가합니다. +puts lots\nof\n\n\n\n\n\nnewlines + + +# 중괄호로 묶인 단어는 닫는 중괄호를 찾을 때 중괄호 앞의 백슬래시가 계산되지 않는다는 점을 제외하고는 특별한 해석이나 대입이 적용되지 않습니다. +set somevar { + This is a literal $ sign, and this \} escaped + brace remains uninterpreted +} + + +# 큰따옴표로 묶인 단어에서 공백 문자는 특수 의미를 잃습니다. +set name Neo +set greeting "Hello, $name" + + +# 변수 이름은 모든 문자열이 될 수 있습니다. +set {first name} New + + +# 중괄호 형식의 변수 대입은 더 복잡한 변수 이름을 처리합니다. +set greeting "Hello, ${first name}" + + +# "set"은 항상 변수 대입 대신 사용할 수 있으며 모든 변수 이름을 처리할 수 있습니다. +set greeting "Hello, [set {first name}]" + + +# 목록을 명령으로 풀려면 확장 연산자 "{*}"를 사용하십시오. 이 두 명령은 동일합니다. +set name Neo +set {*}{name Neo} + + +# 배열은 다른 변수의 컨테이너인 특수 변수입니다. +set person(name) Neo +set person(destiny) {The One} +set greeting "Hello, $person(name)" + + +# "variable"은 변수를 선언하거나 설정하는 데 사용할 수 있습니다. 변수 이름을 확인하기 위해 전역 네임스페이스와 현재 네임스페이스를 모두 사용하는 "set"과 달리 "variable"은 현재 네임스페이스에서만 작동합니다. +variable name New + + +# "namespace eval"은 존재하지 않는 경우 새 네임스페이스를 만듭니다. 네임스페이스에는 루틴과 변수가 모두 포함될 수 있습니다. +namespace eval people { + namespace eval person1 { + variable name Neo + } +} + + +# 변수 이름의 네임스페이스 구성 요소를 구분하려면 두 개 이상의 콜론을 사용하십시오. +namespace eval people { + set greeting "Hello $person1::name" +} + +# 루틴 이름의 네임스페이스 구성 요소를 구분하려면 두 개 이상의 콜론을 사용하십시오. +proc people::person1::speak {} { + puts {I am The One.} +} + +# 정규화된 이름은 두 개의 콜론으로 시작합니다. +set greeting "Hello $::people::person1::name" + + + +############################################################################### +## 3. 더 이상 구문 없음 +############################################################################### + +# 다른 모든 기능은 루틴을 통해 구현됩니다. 이 시점부터 새로운 구문은 없습니다. Tcl에 대해 배울 다른 모든 것은 개별 루틴의 동작과 해당 루틴이 인수에 할당하는 의미에 관한 것입니다. + + + +############################################################################### +## 4. 변수 및 네임스페이스 +############################################################################### + +# 각 변수와 루틴은 일부 네임스페이스와 연결됩니다. + +# 아무것도 할 수 없는 인터프리터를 만들려면 전역 네임스페이스를 삭제하십시오. 그런 일을 하는 것은 별로 유용하지 않지만 Tcl의 본질을 보여줍니다. 전역 네임스페이스의 이름은 실제로 빈 문자열이지만 이를 나타내는 유일한 방법은 정규화된 이름으로 사용하는 것입니다. 시도해 보려면 이 루틴을 호출하십시오. +proc delete_global_namespace {} { + namespace delete :: +} + +# "set"은 항상 전역 네임스페이스와 현재 네임스페이스를 모두 주시하므로 변수를 선언하거나 변수에 값을 할당하려면 "variable"을 사용하는 것이 더 안전합니다. "name"이라는 변수가 전역 네임스페이스에 이미 있는 경우 여기에서 "set"을 사용하면 현재 네임스페이스의 변수 대신 전역 변수에 값을 할당하는 반면 "variable"은 현재 네임스페이스에서만 작동합니다. +namespace eval people { + namespace eval person1 { + variable name Neo + } +} + +# 네임스페이스에 변수가 선언되면 [set]은 전역 네임스페이스에서 동일한 이름의 변수를 보는 대신 해당 변수를 봅니다. +namespace eval people { + namespace eval person1 { + variable name + set name Neo + } +} + +# 그러나 "set"이 새 변수를 만들어야 하는 경우 항상 현재 네임스페이스를 기준으로 수행합니다. +unset name +namespace eval people { + namespace eval person1 { + set name neo + } + +} +set people::person1::name + + +# 절대 이름은 항상 전역 네임스페이스의 이름(빈 문자열)으로 시작하고 그 뒤에 두 개의 콜론이 옵니다. +set ::people::person1::name Neo + + +# 프로시저 내에서 "variable"은 현재 네임스페이스의 변수를 로컬 범위에 연결합니다. +namespace eval people::person1 { + proc fly {} { + variable name + puts "$name is flying!" + } +} + + + + +############################################################################### +## 5. 내장 루틴 +############################################################################### + +# 수학은 "expr"로 수행할 수 있습니다. +set a 3 +set b 4 +set c [expr {$a + $b}] + +# "expr"은 자체적으로 변수 대입을 수행하므로 Tcl이 먼저 변수 대입을 수행하지 않도록 표현식을 중괄호로 묶습니다. 자세한 내용은 "https://wiki.tcl-lang.org/page/Brace+your+expr-essions"를 참조하십시오. + + +# "expr"은 변수 및 스크립트 대입을 이해합니다. +set c [expr {$a + [set b]}] + + +# "expr"은 수학 함수 집합을 제공합니다. +set c [expr {pow($a,$b)}] + + +# 수학 연산자는 ::tcl::mathop 네임스페이스에서 루틴으로 사용할 수 있습니다. +::tcl::mathop::+ 5 3 + +# 다른 네임스페이스에서 루틴을 가져올 수 있습니다. +namespace import ::tcl::mathop::+ +set result [+ 5 3] + + +# 숫자가 아닌 값은 인용해야 하며 "eq"와 같은 연산자를 사용하여 연산을 문자열 비교로 제한할 수 있습니다. +set name Neo +expr {{Bob} eq $name} + +# 일반 연산자는 숫자 연산이 불가능한 경우 문자열 비교로 대체됩니다. +expr {{Bob} == $name} + + +# "proc"은 새 루틴을 만듭니다. +proc greet name { + return "Hello, $name!" +} + +# 여러 매개변수를 지정할 수 있습니다. +proc greet {greeting name} { + return "$greeting, $name!" +} + + +# 앞에서 언급했듯이 중괄호는 코드 블록을 구성하지 않습니다. "proc"에 대한 세 번째 인수를 포함하여 모든 값은 문자열입니다. 이전 명령은 중괄호 없이 다시 작성할 수 있습니다. +proc greet greeting\ name return\ \"\$greeting,\ \$name!\" +# " + + + +# 마지막 매개변수가 리터럴 값 "args"이면 루틴에 전달된 모든 추가 인수가 목록으로 수집되어 "args"에 할당됩니다. +proc fold {cmd first args} { + foreach arg $args { + set first [$cmd $first $arg] + } + return $first +} +fold ::tcl::mathop::* 5 3 3 ;# -> 45 + + +# 조건부 실행은 루틴으로 구현됩니다. +if {3 > 4} { + puts {This will never happen} +} elseif {4 > 4} { + puts {This will also never happen} +} else { + puts {This will always happen} +} + + +# 루프는 루틴으로 구현됩니다. "for"에 대한 첫 번째 및 세 번째 인수는 스크립트로 처리되는 반면 두 번째 인수는 표현식으로 처리됩니다. +set res 0 +for {set i 0} {$i < 10} {incr i} { + set res [expr {$res + $i}] +} +unset res + + +# "while"에 대한 첫 번째 인수도 표현식으로 처리됩니다. +set i 0 +while {$i < 10} { + incr i 2 +} + + +# 목록은 문자열이며 목록의 항목은 공백으로 구분됩니다. +set amounts 10\ 33\ 18 +set amount [lindex $amounts 1] + +# 목록 항목의 공백은 인용해야 합니다. +set inventory {"item 1" item\ 2 {item 3}} + + +# 목록을 수정할 때 목록 루틴을 사용하는 것이 일반적으로 더 좋습니다. +lappend inventory {item 1} {item 2} {item 3} + + +# 중괄호와 백슬래시를 사용하여 목록에서 더 복잡한 값을 서식 지정할 수 있습니다. 목록은 개행 문자와 세미콜론 문자가 특수 의미를 잃고 스크립트나 변수 대입이 없다는 점을 제외하고는 스크립트와 똑같이 보입니다. 다음 목록에는 세 개의 항목이 있습니다. +set values { + + one\ two + + {three four} + + five\{six + +} + + +# 모든 값과 마찬가지로 목록은 문자열이므로 목록의 서식을 손상시킬 위험이 있으므로 문자열 연산을 수행할 수 있습니다. +set values {one two three four} +set values [string map {two \{} $values] ;# $values는 더 이상 제대로 서식이 지정된 목록이 아닙니다. + + +# 제대로 서식이 지정된 목록을 얻는 확실한 방법은 "list" 루틴을 사용하는 것입니다. +set values [list one \{ three four] +lappend values { } ;# 목록에 단일 공백을 항목으로 추가 + + +# "eval"을 사용하여 값을 스크립트로 평가합니다. +eval { + set name Neo + set greeting "Hello, $name" +} + + +# 목록은 항상 단일 명령으로 구성된 스크립트로 "eval"에 전달될 수 있습니다. +eval {set name Neo} +eval [list set greeting "Hello, $name"] + + +# 따라서 "eval"을 사용할 때 "list"를 사용하여 원하는 명령을 구성하십시오. +set command {set name} +lappend command {Archibald Sorbisol} +eval $command + + +# 일반적인 실수는 명령을 구성할 때 목록 함수를 사용하지 않는 것입니다. +set command {set name} +append command { Archibald Sorbisol} +try { + eval $command ;# 여기 오류는 {set name Archibald Sorbisol}에서 "set"에 대한 인수가 너무 많다는 것입니다. +} on error {result eoptions} { + puts [list {received an error} $result] +} + +# 이 실수는 "subst"에서도 쉽게 발생할 수 있습니다. + +set replacement {Archibald Sorbisol} +set command {set name $replacement} +set command [subst $command] +try { + eval $command ;# 이전과 동일한 오류: {set name Archibald Sorbisol}에서 "set"에 대한 인수가 너무 많습니다. +} trap {TCL WRONGARGS} {result options} { + puts [list {received another error} $result] +} + + +# "list"는 대입을 위해 값을 올바르게 서식 지정합니다. +set replacement [list {Archibald Sorbisol}] +set command {set name $replacement} +set command [subst $command] +eval $command + + +# "list"는 스크립트로 대입하기 위해 값을 서식 지정하는 데 일반적으로 사용됩니다. 아래에 이에 대한 몇 가지 예가 있습니다. + + +# "apply"는 두 항목 목록을 루틴으로 평가합니다. +set cmd {{greeting name} { + return "$greeting, $name!" +}} +apply $cmd Whaddup Neo + +# 세 번째 항목을 사용하여 루틴을 적용할 네임스페이스를 지정할 수 있습니다. +set cmd [list {greeting name} { + return "$greeting, $name!" +} [namespace current]] +apply $cmd Whaddup Neo + + +# "uplevel"은 호출 스택의 상위 수준에서 스크립트를 평가합니다. +proc greet {} { + uplevel {puts "$greeting, $name"} +} + +proc set_double {varname value} { + if {[string is double $value]} { + uplevel [list variable $varname $value] + } else { + error [list {not a double} $value] + } +} + + +# "upvar"는 호출 스택의 현재 수준에 있는 변수를 상위 수준의 변수에 연결합니다. +proc set_double {varname value} { + if {[string is double $value]} { + upvar 1 $varname var + set var $value + } else { + error [list {not a double} $value] + } +} + + +# 내장 "while" 루틴을 제거하고 "proc"을 사용하여 새 루틴을 정의합니다. +rename ::while {} +# 처리는 연습 문제로 남겨둡니다. +proc while {condition script} { + if {[uplevel 1 [list expr $condition]]} { + uplevel 1 $script + tailcall [namespace which while] $condition $script + } +} + + +# "coroutine"은 새 호출 스택, 해당 호출 스택에 들어갈 새 루틴을 만들고 해당 루틴을 호출합니다. "yield"는 해당 스택에서 평가를 일시 중단하고 호출 스택으로 제어를 반환합니다. +proc countdown count { + # 코루틴 생성자에게 무언가를 다시 보내고, 당분간 이 호출 스택을 일시 중지합니다. + yield [info coroutine] + + while {$count > 1} { + yield [incr count -1] + } + return 0 +} +coroutine countdown1 countdown 3 +coroutine countdown2 countdown 5 +puts [countdown1] ;# -> 2 +puts [countdown2] ;# -> 4 +puts [countdown1] ;# -> 1 +puts [countdown1] ;# -> 0 +catch { + puts [countdown1] ;# -> invalid command name "countdown1" +} cres copts +puts $cres +puts [countdown2] ;# -> 3 + + +# 코루틴 스택은 서로 제어를 양보할 수 있습니다. + +proc pass {whom args} { + return [yieldto $whom {*}$args] +} + +coroutine a apply {{} { + yield + set result [pass b {please pass the salt}] + puts [list got the $result] + set result [pass b {please pass the pepper}] + puts [list got the $result] +}} + +coroutine b apply {{} { + set request [yield] + while 1 { + set response [pass c $request] + puts [list [info coroutine] is now yielding] + set request [pass a $response] + } +}} + +coroutine c apply {{} { + set request [yield] + while 1 { + if {[string match *salt* $request]} { + set request [pass b salt] + } else { + set request [pass b huh?] + } + } +}} + +# 움직이기 +a +``` + +## 참고 자료 + +[공식 Tcl 문서](https://www.tcl-lang.org) + +[Tcl 위키](https://wiki.tcl-lang.org) + +[Tcl 서브레딧](http://www.reddit.com/r/Tcl) diff --git a/ko/tcsh.md b/ko/tcsh.md new file mode 100644 index 0000000000..2304ff153a --- /dev/null +++ b/ko/tcsh.md @@ -0,0 +1,779 @@ +--- +name: tcsh +filename: LearnTCSH.csh +contributors: + - ["Nicholas Christopoulos", "https://github.com/nereusx"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +tcsh("tee-see-shell")는 C 셸(csh)을 기반으로 하고 호환되는 유닉스 셸입니다. +본질적으로 프로그래밍 가능한 명령줄 완성, 명령줄 편집 및 몇 가지 다른 기능이 있는 C 셸입니다. +FreeBSD와 같은 BSD 기반 시스템의 기본 루트 셸입니다. + +오늘날 거의 모든 Linux 배포판과 BSD는 원래 csh 대신 tcsh를 사용합니다. 대부분의 경우 csh는 tcsh를 가리키는 심볼릭 링크입니다. +이는 tcsh가 csh와 하위 호환되고 후자는 더 이상 유지 관리되지 않기 때문입니다. + +- [TCSH 홈페이지](http://www.tcsh.org/) +- [TCSH 위키백과](https://en.wikipedia.org/wiki/Tcsh) +- [TCSH 매뉴얼 페이지](http://www.tcsh.org/tcsh.html/top.html) +- ["C 셸 소개", William Joy](https://docs.freebsd.org/44doc/usd/04.csh/paper.html) +- [TCSH 버그 리포트 및/또는 기능 요청](https://bugs.gw.com/) + +추가 파일: +[tcsh 도움말 명령어 (132x35 터미널 크기용)](https://github.com/nereusx/dotfiles/blob/master/csh-help), +[나의 ~/.tcshrc](https://github.com/nereusx/dotfiles/blob/master/.tcshrc) + +```tcsh +#!/bin/tcsh +# 스크립트의 첫 번째 줄은 시스템에 스크립트 실행 방법을 알려주는 shebang입니다. +# http://en.wikipedia.org/wiki/Shebang_(Unix) +# TCSH는 shebang을 이해하지 못하는 시스템에서 이를 에뮬레이트합니다. + +# 대부분의 경우 `#!/bin/tcsh -f`를 사용합니다. `-f` 옵션은 +# 리소스나 시작 파일을 로드하지 않고, 명령어 해싱을 수행하지 않으므로 +# 더 빨리 시작됩니다. + +# --- echo 명령어 -------------------------------------------------------- +# `echo`는 각 단어를 셸의 표준 출력에 공백으로 구분하고 +# 줄 바꿈으로 종료하여 씁니다. echo_style 셸 변수는 +# 플래그와 이스케이프 시퀀스를 에뮬레이트(또는 에뮬레이트하지 않도록)하도록 설정할 수 있습니다. + +# echo_style 값 표시 +echo $echo_style + +# `echo`가 백슬래시 문자와 `-n` 옵션(줄 바꿈 없음)을 지원하도록 활성화 +# 이것은 tcsh의 기본값이지만, 배포판에서 변경할 수 있습니다. Slackware는 +# 그렇게 했습니다. +set echo_style = both + +# "Hello world" 출력 +echo Hello world +echo "Hello world" +echo 'Hello world' +echo `echo Hello world` + +# "twonlines"를 한 줄에 출력 +echo two\nlines + +# 두 줄 출력 +echo "two\nlines" +echo 'two\nlines' + +# --- 기본 구문 ------------------------------------------------------------ + +# 특수 문자(공백이나 탭 포함)는 앞에 백슬래시 `\`를 붙여 +# 특수 의미를 갖지 않도록 할 수 있습니다. +# 마지막 히스토리 명령어를 표시합니다 +echo !! +# 이것은 그렇지 않습니다 +echo \!\! + +# 작은따옴표도 특수 문자 확장을 방지하지만, `!`나 백슬래시 같은 +# 일부 문자는 우선순위가 더 높습니다 +# `$` (변수 값)는 확장되지 않습니다 +echo '$1 tip' +# `!` (히스토리)는 확장됩니다 +echo '!!' + +# 백쿼트로 묶인 문자열은 실행되고 그 결과로 대체됩니다. +echo `ls` + +# 세미콜론은 명령어를 구분합니다 +echo 'first line'; echo 'second line' + +# 조건부 실행도 있습니다 +echo "Always executed" || echo "Only executed if the first command fails" +echo "Always executed" && echo "Only executed if the first command does NOT fail" + +# 괄호로 묶인 명령어는 항상 서브셸에서 실행됩니다, + +# 예: 프로젝트를 생성하고 설치하는 동안 완료되었음을 알립니다. +make && ( espeak "BOSS, compilation finished"; make install ) + +# 홈 디렉토리를 출력하지만 원래 있던 위치에 그대로 둡니다 +(cd; pwd); pwd + +# tcsh 맨페이지 문서 읽기 +man tcsh + +# --- 변수 --------------------------------------------------------------- +# 셸은 변수 목록을 유지하며, 각 변수는 0개 이상의 단어로 구성된 목록을 값으로 가집니다. +# 셸 변수의 값은 `set` 및 `unset` 명령어로 표시하고 변경할 수 있습니다. +# 시스템은 자체 "환경" 변수 목록을 유지합니다. +# 이는 `printenv`, `setenv`, `unsetenv`로 표시하고 변경할 수 있습니다. +# `setenv`의 구문은 POSIX sh와 유사합니다. + +# 값을 할당하거나 아무것도 할당하지 않으면 변수가 생성됩니다 +# 아무것도 할당하지 않음 +set var +# 숫자 값 할당 +# '@'는 표현식이 산술임을 나타냅니다. 'set'과 유사하게 작동하지만 +# 오른쪽 값은 숫자 표현식이 될 수 있습니다. +@ var = 1 + 2 +# 문자열 값 할당 +set var = "Hello, I am the contents of 'var' variable" +# 프로그램 출력 할당 +set var = `ls` + +# 변수 제거 +unset var +# 변수 `var`가 존재하면 1(참)을 출력하고, 그렇지 않으면 0(거짓)을 출력합니다 +echo $?var +# 모든 변수와 그 값을 출력합니다 +set + +# 'var'의 내용을 출력합니다 +echo $var; +echo "$var"; +# 문자열 `$var`를 출력합니다 +echo \$var +echo '$var' +# 필요할 때 변수를 나머지 부분과 분리하기 위해 중괄호를 사용할 수 있습니다 +set num = 12; echo "There ${num}th element" + +# 값의 문자 수를 출력합니다: 6 +set var = '123456'; echo $%var + +### 리스트 +# 값 목록 할당 +set var = ( one two three four five ) +# 모든 요소 출력: one two three four five +echo $var +echo $var[*] +# 요소 개수 출력: 5 +echo $#var +# 인덱싱된 요소 출력; 두 번째 요소 출력: two +echo $var[2] +# 요소 범위 출력; 2번째부터 3번째까지 출력: two, three +echo $var[2-3] +# 3번째부터 모든 요소 출력: three four five +echo $var[3-] +# 3번째 요소까지 모두 출력: one two three +echo $var[-3] + +### 특수 변수 +# $argv 명령줄 인수 목록 +# $argv[0] 이 파일 이름 (스크립트 파일의 파일) +# $# $0, $n, $*는 $#argv, $argv[0], $argv[n], $argv[*]와 동일합니다 +# $status, $? 마지막으로 실행된 명령어의 종료 코드 +# $_ 이전 명령줄 +# $! 이 셸에서 시작된 마지막 백그라운드 프로세스의 PID +# $$ 스크립트의 PID + +# $path, $PATH 실행 파일을 찾을 디렉토리 목록 +# $home, $HOME 사용자의 홈 디렉토리, `~`를 대신 사용할 수도 있습니다 +# $uid 사용자 로그인 ID +# $user 사용자 로그인 이름 +# $gid 사용자 그룹 ID +# $group 사용자 그룹 이름 +# $cwd, $PWD 현재/작업 디렉토리 출력 +# $owd 이전 작업 디렉토리 +# $tcsh tcsh 버전 +# $tty 현재 tty; Linux 콘솔의 경우 ttyN, X의 터미널 에뮬레이터의 경우 pts/N +# $term 터미널 유형 +# $verbose 설정되면 각 명령어의 단어를 출력합니다. +# `-v` 명령줄 옵션으로도 설정할 수 있습니다. +# $loginsh 설정되면 로그인 셸입니다 + +# 팁: $?0은 대화형 셸에서 항상 거짓입니다 +# 팁: $?prompt는 비대화형 셸에서 항상 거짓입니다 +# 팁: `$?tcsh`가 설정되지 않았다면, 원래 `csh`나 다른 것을 실행하고 있는 것입니다; +# `echo $shell`을 시도해보세요 +# 팁: `$verbose`는 스크립트 디버깅에 유용합니다 +# 참고: `$PWD`와 `$PATH`는 `$cwd`와 `$pwd`와 자동으로 동기화됩니다. + +# --- 변수 수정자 ------------------------------------------------------ +# 구문: ${var}:m[:mN] +# 여기서 은 다음과 같습니다: +# h : 디렉토리 t : 파일 이름 r : 확장자 제거 e : 확장자 +# u : 첫 소문자를 대문자로 +# l : 첫 대문자를 소문자로 +# p : 출력하지만 실행하지는 않음 (hist) +# q : 대체된 단어를 인용하여 추가 대체를 방지 +# x : q와 같지만 공백에서 단어로 나눔 +# g : 다음 수정자를 각 단어에 한 번 적용 +# a : 다음 수정자를 단일 단어에 가능한 한 많이 적용 +# s/l/r/ : `l`을 찾아 `r`로 바꿈, 정규식 아님; `r`의 `&`는 `l`로 대체됨 +# & : 이전 대체 반복 + +# 이 파일로 시작 +set f = ~/Documents/Alpha/beta.txt +# ~/Documents/Alpha/beta 출력 +echo $f:r +# ~/Documents/Alpha 출력 +echo $f:h +# beta.txt 출력 +echo $f:t +# txt 출력 +echo $f:e +# beta 출력 +echo $f:t:r +# Beta 출력 +echo $f:t:r:u +# Biota 출력 +echo $f:t:r:u:s/eta/iota/ + +# --- 리다이렉션 ------------------------------------------------------------- + +# file.txt를 만들고 표준 출력을 씁니다 +echo 'this string' > file.txt +# file.txt를 만들고 표준 출력과 표준 에러를 씁니다 +echo 'this string' >& file.txt +# 표준 출력을 file.txt에 추가합니다 +echo 'this string' >> file.txt +# 표준 출력과 표준 에러를 file.txt에 추가합니다 +echo 'this string' >>& file.txt +# file.txt에서 표준 입력을 리다이렉션합니다 +cat < file.txt +# 키보드 입력; 입력 줄을 변수 `x`에 저장합니다 +set x = $< +# 여기에 문서; +cat << LABEL +...text here... +LABEL + +# 팁: 표준 에러를 분리하는 방법은 다음과 같습니다: +(grep 'AGP' /usr/src/linux/Documentation/* > output-file.txt) >& error-file.txt + +# 예: 표준 입력에서 이름을 읽고 인사 메시지를 표시합니다 +echo -n "Enter your name: " +set name = $< +echo "Greetings $name" + +# --- 표현식 ------------------------------------------------------------ + +# 연산자: +# == 같음 != 같지 않음 ! 부정 +# > 보다 큼 < 보다 작음 >= 크거나 같음 <= 작거나 같음 +# && 논리 AND || 논리 OR + +if ( $name != $user ) then + echo "Your name isn't your username" +else + echo "Your name is your username" +endif + +# 한 줄 형식 +if ( $name != $user ) echo "Your name isn't your username" + +# 참고: $name이 비어 있으면 tcsh는 위 조건을 다음과 같이 봅니다: +# if ( != $user ) ... +# 이것은 잘못된 구문입니다 +# tcsh에서 잠재적으로 비어 있는 변수를 사용하는 "안전한" 방법은 다음과 같습니다: +# if ( "$name" != $user ) ... +# $name이 비어 있을 때 tcsh는 이것을 다음과 같이 봅니다: +# if ( "" != $user ) ... +# 이것은 예상대로 작동합니다 + +# 조건부 실행도 있습니다 +echo "Always executed" || echo "Only executed if the first command fails" +echo "Always executed" && echo "Only executed if the first command does NOT fail" + +# if 문에서 &&와 ||를 사용하려면 여러 쌍의 +# 대괄호가 필요하지 않습니다: +if ( "$name" == "Steve" && "$age" == 15 ) then + echo "This will run if $name is Steve AND $age is 15." +endif + +if ( "$name" == "Daniya" || "$name" == "Zach" ) then + echo "This will run if $name is Daniya OR Zach." +endif + +# 문자열 일치 연산자 ( `=~` 및 `!~` ) +# '==' '!=' '=~' 및 '!~' 연산자는 인수를 문자열로 비교합니다; +# 다른 모든 연산자는 숫자에 대해 작동합니다. '=~' 및 '!~' 연산자는 '!=' +# 및 '=='와 같지만 오른쪽이 왼쪽 피연산자와 일치하는 +# glob-패턴이라는 점이 다릅니다. + +if ( $user =~ ni[ck]* ) echo "Greetings Mr. Nicholas." +if ( $user !~ ni[ck]* ) echo "Hey, get out of Nicholas' PC." + +# 산술 표현식은 다음 형식으로 표시됩니다: +@ result = 10 + 5 +echo $result + +# 산술 연산자 +# +, -, *, /, % +# +# 괄호로 묶어야 하는 산술 연산자 +# !, ~, |, &, ^, ~, <<, >>, +# 비교 및 논리 연산자 +# +# 모든 연산자는 C와 동일합니다. + +# 숫자 표현식에 공백이 필요하다는 것은 잘 문서화되어 있지 않습니다; +# 또한, `@`에는 자체 파서가 있으며, 표현식이 괄호로 묶여 있을 때 잘 작동하는 것 같습니다. +# 그렇지 않으면 기본 파서가 활성 상태인 것 같습니다. +# 괄호는 주위에 공백이 필요하며, 이것은 문서화되어 있습니다. + +# 잘못됨 +@ x = $y+1 +@ x = 0644 & 022; echo $x +@ x = (0644 & 022) +1; echo $x +@ x = (0644 & 022)+ 1; echo $x +@ x = ( ~077 ); echo $x + +# 올바름 +@ x = $y + 1 +@ x = ( 0644 & 022 ) + 1; echo $x +@ x = ( ~ 077 ); echo $x +@ x = ( ~ 077 | 022 ); echo $x +@ x = ( ! 0 ); echo $x + +# C의 연산자 ++ 및 --는 할당이 없는 경우 지원됩니다 +@ result ++ + +# 수학을 하기 위해 만들어진 셸은 없습니다; +# 기본 연산을 제외하고 백슬래시와 함께 외부 명령어를 사용하십시오. +# +# 저는 calc를 최상의 옵션으로 제안합니다. +# (http://www.isthe.com/chongo/tech/comp/calc/) +# +# 두 번째 옵션으로 표준 Unix의 bc +# (https://www.gnu.org/software/bc/manual/html_mono/bc.html) +# +# 세 번째 옵션으로 표준 Unix의 AWK +# (https://www.gnu.org/software/gawk/manual/gawk.html) + +# `Perl`, `PHP`, `python` 또는 여러 BASIC을 사용할 수도 있지만, +# 더 빠른 로드 및 실행 결과를 위해 위 유틸리티를 선호하십시오. + +# 실제 예: (StackExchange에서 답변한 내용) +# 요구사항: x := 1001b OR 0110b + +# `tcsh` 표현식에서 (8진수 사용) +@ x = ( 011 | 06 ); echo $x + +# `calc`를 사용하여 동일하게 (원래 요구사항대로 이진수 사용) +set x = `calc '0b1001 | 0b110'`; echo $x + +# --- 파일 조회 연산자 -------------------------------------------------- +# 참고: 내장 `filetest` 명령어는 동일한 작업을 수행합니다. + +#### 불리언 연산자 +# -r 읽기 접근 -w 쓰기 접근 -x 실행 접근 -e 존재 +# -f 일반 파일 -d 디렉토리 -l 심볼릭 링크 -p 명명된 파이프 +# -S 소켓 파일 +# -o 소유권 -z 크기 0 -s 크기 0 아님 +# -u SUID 설정됨 -g SGID 설정됨 -k 스티키 비트 설정됨 +# -b 블록 장치 -c 문자 장치 +# -t 파일(숫자)이 터미널 장치에 대한 열린 파일 디스크립터임 + +# `README` 파일이 존재하면 메시지를 표시합니다 +if ( -e README ) echo "I have already README file" + +# `less` 프로그램이 설치되어 있으면 `more` 대신 사용합니다 +if ( -e `where less` ) then + alias more 'less' +endif + +#### 비-불리언 연산자 +# -Z 파일 크기를 바이트 단위로 반환 +# -M 수정 시간(mtime) 반환 -M: mtime 문자열 반환 +# -A 마지막 접근 시간(atime) 반환 -A: atime 문자열 반환 +# -U 소유자 사용자 ID 반환 -U: 소유자 사용자 이름 반환 +# -G 소유자 그룹 ID 반환 -G: 소유자 그룹 이름 반환 +# -P 권한을 8진수로 반환 -Pmode는 권한과 모드의 AND 결과 반환 + +# 이것은 날짜를 유닉스 시간 정수로 표시합니다: 1498511486 +filetest -M README.md + +# 이것은 "Tue Jun 27 00:11:26 2017"을 표시합니다 +filetest -M: README.md + +# --- 기본 명령어 ---------------------------------------------------------- + +# `chdir` (cd)로 파일 시스템 탐색 +cd path # 작업 디렉토리 변경 +cd # 홈 디렉토리로 변경 +cd - # 이전 디렉토리로 변경 +cd .. # 한 디렉토리 위로 이동 + +# 예: +cd ~/Downloads # 나의 `Downloads` 디렉토리로 이동 + +# `mkdir`를 사용하여 새 디렉토리 생성. +mkdir newdir +# `-p` 플래그는 필요에 따라 새 중간 디렉토리를 생성합니다. +mkdir -p ~/.backup/saves + +# which & where +# csh가 tcsh를 가리키는지 찾기 +ls -lha `which csh` +# csh가 둘 이상의 디렉토리에 설치되어 있는지 찾기 +where csh + +# --- 파이프라인 -------------------------------------------------------------- +# 파이프라인은 각 프로세스의 출력(stdout)이 다음 프로세스의 입력(stdin)으로 +# 직접 공급되도록 표준 스트림으로 함께 연결된 프로세스 시퀀스입니다. +# 이러한 `파이프`는 `|` 특수 문자로 생성되며 유닉스의 가장 강력한 +# 특징 중 하나입니다. + +# 예: +ls -l | grep key | less +# "ls -l"은 프로세스를 생성하고, 그 출력(stdout)은 "grep key" 프로세스의 +# 입력(stdin)으로 파이프됩니다. "less" 프로세스도 마찬가지입니다. + +# `ls`, `grep`, `less`는 유닉스 프로그램이며 자체 맨페이지가 있습니다. +# `파이프` 메커니즘은 커널의 일부이지만 구문과 제어는 +# 셸의 역할이며, 이 경우 tcsh입니다. + +# 참고: Windows에도 `파이프` 메커니즘이 있지만 버그가 있으며, 제가 마지막으로 +# 작업했던 Windows XP SP3 API32까지 모든 버전에 대해 서명했습니다. +# Microsoft는 이를 부인했지만, 프로세스 간 통신에 일반적인 방법이기 때문에 +# 잘 알려진 버그입니다. 작은 I/O의 경우 잘 작동합니다. +# tcsh는 grep, GCC, Perl과 함께 DOS(EMX DOS 확장기 사용) 및 +# 나중에 Windows(1998)로 포팅된 최초의 유닉스 프로그램 중 하나입니다. + +# 예: 이것은 tcsh를 PostScript로 변환하고 Okular로 보여줍니다 +zcat /usr/man/man1/tcsh.1.gz | groff -Tps -man | okular - + +# 더 나은 버전 +zcat `locate -b -n 1 '\tcsh.1.gz'` | groff -Tps -man | okular - + +# 훨씬 더 나은 버전 +set page = tcsh; set loc = (locate -b -n 1 "\\\\"${page}".1.gz"); + zcat `eval $loc` | groff -Tps -man | okular - + +# 동일, 맨페이지 pdf를 생성하도록 수정 +set page = tcsh; set loc = (locate -b -n 1 "\\\\"${page}".1.gz"); + zcat `eval $loc` | groff -Tps -man | ps2pdf - ${page}.pdf + +# 동일, 이제 ${page}.pdf도 표시 +set page = tcsh; set loc = (locate -b -n 1 "\\\\"${page}".1.gz"); + zcat `eval $loc` | groff -Tps -man | ps2pdf - ${page}.pdf && okular tcsh.pdf + +# 참고: `okular`는 KDE 환경의 기본 응용 프로그램이며 포스트스크립트 및 +# pdf 파일을 표시합니다. 좋아하는 PDF 뷰어로 교체할 수 있습니다. +# `zcat`, `locate`, `groff`는 모든 유닉스에서 일반적인 프로그램입니다. `ps2pdf` +# 프로그램은 널리 사용되는 `ghostscript` 패키지의 일부입니다. + +# --- 제어 흐름 ------------------------------------------------------------ + +#### IF-THEN-ELSE-ENDIF +# 구문: +# if ( expr ) then +# ... +# [else if ( expr2 ) then +# ...] +# [else +# ...] +# endif +# +# 지정된 `expr`이 참이면 첫 번째 else까지의 명령어가 실행됩니다; +# 그렇지 않고 `expr2`가 참이면 두 번째 else까지의 명령어가 실행되는 식입니다. +# else-if 쌍은 얼마든지 가능하며, endif는 하나만 필요합니다. +# +# 한 줄 형식: +# +# if ( expr ) command +# +# `expr`이 참으로 평가되면 명령어가 실행됩니다. +# `command`는 단순 명령어여야 하며, 별칭, 파이프라인, 명령어 목록 +#, 또는 괄호로 묶인 명령어 목록이 아니어야 합니다. 간단히 말해 사용을 피하십시오. +# +# 버그: expr이 거짓이고 명령어가 실행되지 않더라도 입/출력 리다이렉션이 발생합니다. +# + +# 비대화형 셸인지 확인하고 참이면 종료 +if ( $?USER == 0 || $?prompt == 0 ) exit + +# 로그인 셸인지 확인 +if ( $?loginsh ) then + # 리눅스 콘솔에 있는지 확인 (X의 터미널 아님) + if ( $tty =~ tty* ) then + # 키패드 응용 프로그램 키 활성화 (man console_codes) + echo '\033=' + endif +endif + +#### SWITCH-ENDSW +# 구문: +# switch ( expr ) +# case pattern: +# ... +# [breaksw] +# [default: +# ...] +# endsw +# +# tcsh는 C의 switch와 유사하게 작동하는 case 문을 사용합니다. +# 각 case 레이블은 먼저 명령어와 파일 이름이 확장된 지정된 문자열과 +# 순차적으로 일치합니다. 파일 메타문자 `*`, `?`, `[...]`를 case 레이블에 +# 사용할 수 있습니다. 레이블이 일치하지 않으면 정의된 경우 기본 레이블 +# 다음에 실행이 시작됩니다. +# `breaksw` 명령어는 endsw 다음에 실행을 계속하도록 합니다. 그렇지 않으면 +# C에서처럼 제어가 case 레이블과 기본 레이블을 통과할 수 있습니다. + +switch ( $var ) +case *.[1-9]: +case *.[1-9].gz: + echo "$var is a man-page." + breaksw +case *gz: + echo "$var is gzipped" + breaksw +default: + file $var +endsw + +#### FOREACH-END +# 구문: +# foreach name ( wordlist ) +# ... +# [break | continue] +# end +# +# 변수 `name`을 `wordlist`의 각 멤버로 순차적으로 설정하고 +# 이 명령어와 일치하는 `end` 키워드 사이의 명령어 시퀀스를 실행합니다. +# `continue` 키워드는 맨 위로 돌아가 다음 요소로 점프하고, `break` 키워드는 +# 루프를 종료합니다. +# +# 버그: `foreach`는 끝을 찾을 때 here document를 무시하지 않습니다. + +# 예: 1에서 10까지 세기 +foreach i ( `seq 1 10` ) + echo $i +end + +# 예: 목록의 모든 파일 유형 +foreach f ( a.txt b.txt c.txt ) + cat $f +end + +# 예: wma를 ogg로 변환 +foreach f ( *.wma ) + ffmpeg -i "$f" "$f:r".ogg +end + +#### WHILE-END +# while ( expr ) +# ... +# [break | continue] +# end +# +# `expr`이 0이 아닌 값으로 평가되는 동안 `while`과 일치하는 `end` 사이의 +# 명령어를 실행합니다. `break`와 `continue`를 사용하여 루프를 조기에 +# 종료하거나 계속할 수 있습니다. + +# 1에서 10까지 세기 +set num = 1 +while ( $num <= 10 ) + echo $num + @ num ++ +end + +# CWD의 모든 디렉토리 출력 +set lst = ( * ) +while ( $#lst ) + if ( -d $lst[1] ) echo $lst[1] is directory + shift lst +end + +# 명령줄 인수를 옵션 또는 매개변수로 분리 +set options +set params +set lst = ( $* ) +while ( $#lst ) + if ( "$lst[1]" =~ '-*' ) then + set options = ( $options $lst[1] ) + else + set params = ( $params $lst[1] ) + endif + shift lst +end +echo 'options =' $options +echo 'parameters =' $params + +#### REPEAT +# 구문: repeat count command +# +# 위 한 줄 `if` 문의 명령어와 동일한 제한을 받는 지정된 명령어가 +# count 횟수만큼 실행됩니다. +# `count`가 0이더라도 I/O 리다이렉션은 정확히 한 번 발생합니다. +# +# 팁: 대부분의 경우 `while`을 선호하십시오 + +repeat 3 echo "ding dong" + +# --- 함수 --------------------------------------------------------------- +# tcsh에는 함수가 없지만 표현식 구문이 `alias`를 함수로 사용할 만큼 +# 충분히 발전했습니다. 다른 방법은 재귀입니다 + +# 별칭 인수 선택자; 별칭에 제공된 인수를 가져와 참조하는 명령어에 +# 적용하는 기능을 정의하는 기능입니다. +# Tcsh는 이 기능을 제공하는 유일한 셸입니다. +# +# \!# 별칭/명령어 자체를 포함한 모든 인수에 대한 인수 선택자; +# 인수를 제공할 필요는 없습니다. +# \!* 별칭/명령어를 제외한 모든 인수에 대한 인수 선택자; +# 인수를 제공할 필요는 없습니다. +# \!$ 마지막 인수에 대한 인수 선택자; 인수를 제공할 필요는 없지만, +# 아무것도 제공되지 않으면 별칭 이름이 마지막 인수로 간주됩니다. +# \!^ 첫 번째 인수에 대한 인수 선택자; 인수를 반드시 제공해야 합니다. +# \!:n n번째 인수에 대한 인수 선택자; 인수를 반드시 제공해야 합니다; +# n=0은 별칭/명령어 이름을 나타냅니다. +# \!:m-n m번째부터 n번째까지의 인수에 대한 인수 선택자; +# 인수를 반드시 제공해야 합니다. +# \!:n-$ n번째부터 마지막까지의 인수에 대한 인수 선택자; +# 적어도 n번째 인수는 반드시 제공해야 합니다. + +# 디렉토리를 변경할 때 내용이 즉시 표시되도록 cd 명령어에 별칭을 지정합니다. +alias cd 'cd \!* && ls' + +# --- 재귀 방식 --- 시작 --- +#!/bin/tcsh -f +set todo = option1 +if ( $#argv > 0 ) then + set todo = $argv[1] +endif + +switch ( $todo ) +case option1: +# ... + $0 results + breaksw +case option2: +# ... + $0 results + breaksw +case results: + echo "print the results here" +# ... + breaksw +default: + echo "Unknown option: $todo" +# exit 0 +endsw +# --- 재귀 방식 --- 끝 --- + +# --- 예제 ---------------------------------------------------------------- + +# 이 스크립트는 인수가 설정되지 않은 경우 사용 가능한 전원 상태를 출력합니다; +# 그렇지 않으면 $argv[1]의 상태를 설정합니다 +# --- 전원 상태 스크립트 --- 시작 -------------------------------------------- +#!/bin/tcsh -f +# 매개변수 가져오기 (없으면 "help") +set todo = help +if ( $#argv > 0 ) then + set todo = $argv[1] +endif +# 사용 가능한 옵션 +set opts = `cat /sys/power/state` +# 알려져 있습니까? +foreach o ( $opts ) + if ( $todo == $o ) then + # 찾음; 실행 + echo -n $todo > /sys/power/state + break + endif +end +# 도움말 출력 및 종료 +echo "usage: $0 [option]" +echo "available options on kernel: $opts" +# --- 전원 상태 스크립트 --- 끝 ---------------------------------------------- + +# 비밀 숫자 맞추기 게임 +# --- secretnum.csh --- 시작 ------------------------------------------------- +#!/bin/tcsh -f +set secret=`shuf -i1-100 -n1` +echo "I have a secret number from 1 up to 100" +while ( 1 ) + echo -n "Guess: " + set guess = $< + if ( $secret == $guess ) then + echo "You found it" + exit 1 + else + if ( $secret > $guess ) then + echo "its greater" + else if ( $secret < $guess ) then + echo "its lesser" + endif + endif + endif +end +# --- secretnum.csh --- 끝 --------------------------------------------------- + +# ----------------------------------------------------------------------------- +# 부록 + +#### [T]CSH에 대하여: +# * CSH는 버그로 악명이 높습니다; +# * 또한 고급 대화형 모드로도 유명합니다. +# * TCSH는 가장 진보된 완성 하위 시스템을 갖춘 것으로 유명합니다. +# * TCSH는 가장 진보된 별칭 하위 시스템을 갖춘 것으로 유명합니다; 별칭은 +# 매개변수를 사용할 수 있으며 종종 함수로 사용될 수 있습니다! +# * TCSH는 더 나은 구문 때문에 사람들에게 잘 알려져 있고 선호됩니다 (저도 마찬가지). +# 모든 셸은 [t]csh, fish, plan9의 셸(rc, ex)을 제외하고 Thomson의 구문을 사용합니다. +# * bash, zsh, 심지어 mksh보다 작고 메모리를 훨씬 적게 소비합니다! +# (memusage 보고서) +# * TCSH에는 여전히 버그가 있습니다. 더 적지만 있습니다. 읽기 쉬운 깨끗한 코드를 작성하면 +# 아무것도 찾을 수 없을 것입니다. 글쎄, 거의 없을 것입니다... 이것은 csh의 구현과 +# 관련이 있습니다. 다른 셸의 구현이 좋다는 의미는 아닙니다. +# * 잘 알려진 셸 중 정규 프로그래밍이 가능한 셸은 없습니다. 스크립트가 +# 커지면 Python, PHP 또는 Perl과 같은 프로그래밍 언어를 사용하십시오 (좋은 +# 스크립팅 언어). +# +# 조언: +# 1. 한 줄 IF에서 리다이렉션을 사용하지 마십시오 (잘 알려진 버그입니다) +# 대부분의 경우 한 줄 IF 사용을 피하십시오. +# 2. 다른 셸의 코드를 망치지 마십시오. c-shell은 다른 셸과 호환되지 않으며 +# 다른 기능과 우선순위를 가지고 있습니다. +# 3. 어떤 언어로든 읽기 쉬운 코드를 작성하는 것처럼 공백을 사용하십시오. +# csh의 버그는 `set x=1`과 `set x = 1`은 작동했지만 `set x =1`은 작동하지 않았습니다! +# 4. 숫자 표현식 사이에 공백이 필요하다는 것은 잘 문서화되어 있습니다; +# 또한 모든 비트 및 단항 연산자를 괄호로 묶으십시오. +# 5. 여러 따옴표, 백슬래시 등으로 거대하고 이상한 표현식을 작성하지 마십시오. +# 일반 프로그래밍에 좋지 않은 습관이며 어떤 셸에서든 위험합니다. +# 6. tcsh를 도와주세요, 여기에 버그를 보고하세요 +# 7. 맨페이지를 읽으십시오. `tcsh`에는 엄청난 수의 옵션과 변수가 있습니다. +# +# 기본적으로 다음 옵션을 활성화하는 것이 좋습니다 +# -------------------------------------------------- +# 비대화형 셸에서도 +# set echo_style=both +# set backslash_quote +# set parseoctal +# unset noclobber +# +# 무엇이든... +# set inputmode=insert +# set autolist +# set listjobs +# set padhour +# set color +# set colorcat +# set nobeep +# set cdtohome +# +# set histdup +# set histlit +# set nohistclop +# +# unset compat_expr +# unset noglob +# unset autologout +# unset time +# unset tperiod +# +# 참고: `backslash_quote`가 설정되면, 그것 없이 작성된 다른 tcsh 스크립트와 +# 호환성 문제가 발생할 수 있습니다. +# +# 참고: `parseoctal`도 마찬가지이지만, 문제가 있는 스크립트를 수정하는 것이 +# 더 좋습니다. +# +# 참고: **초보자 전용** +# 필요한 경우 `path` 디렉토리를 자동으로 다시 스캔할 수 있습니다. (bash처럼) +# set autorehash + +#### 일반적인 별칭 +# alias hist 'history 20' +# alias ll 'ls --color -lha' +# alias today "date '+%d%h%y' +# alias ff 'find . -name ' + +#### 멋진 프롬프트 +# set prompt = "%B%{\033[35m%}%t %{\033[32m%}%n@%m%b %C4 %# " +``` diff --git a/ko/texinfo.md b/ko/texinfo.md new file mode 100644 index 0000000000..189c0c1cba --- /dev/null +++ b/ko/texinfo.md @@ -0,0 +1,179 @@ +--- +name: Texinfo +contributors: + - ["Julien Lepiller", "https://github.com/roptat"] +filename: learntexinfo.texi +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Texinfo는 동일한 소스에서 다양한 유형의 문서를 만드는 데 사용할 수 있는 문서 형식입니다. 주요 용도는 GNU 프로젝트의 문서 매뉴얼 및 정보 페이지를 만드는 것입니다. + +Texinfo는 텍스트와 생성기가 수행해야 할 작업을 지정하는 *@-명령*을 포함하는 마크업 언어입니다. + +## 초기 파일 + +간단한 매뉴얼의 간단한 예: + +``` +\input texinfo +@setfilename simple-document.info +@documentencoding UTF-8 +@settitle simple-document +@c 이것은 주석입니다 +@c 위의 simple-document를 (두 번) 실제 문서 제목으로 바꾸십시오. + +@c Automake가 version.texi를 처리합니다. +@include version.texi + +@copying +Copyright @copyright{} YEAR MY NAME + +@c GFDL은 GNU 프로젝트에 일반적입니다. +@quotation +이 문서를 복사, 배포 및/또는 수정하는 것은 +자유 소프트웨어 재단에서 발행한 GNU 자유 문서 사용 허가서 버전 1.3 또는 +그 이후 버전의 조건에 따라 허용됩니다. 불변 섹션, 앞표지 텍스트 및 +뒷표지 텍스트는 없습니다. 라이선스 사본은 +"GNU 자유 문서 사용 허가서"라는 제목의 섹션에 포함되어 있습니다. +@end quotation +@end copying + +@titlepage +@end titlepage + +@c 이제 실제 내용이 시작됩니다. +@contents + +@c 첫 번째 노드는 항상 Top이어야 합니다. +@node Top +@c 그리고 제목을 지정합니다. +@top simple-document + +이 문서는 Texinfo 기능을 간략하게 설명합니다. + +@c 이것은 목차입니다: +@menu +* Introduction:: 이 장의 간략한 요약 + +@detailmenu +--- 자세한 노드 목록 --- + +Introduction + +* Formatting:: 텍스트를 멋지게 서식 지정하는 방법 +* Links:: 다른 리소스, 페이지 또는 매뉴얼에 연결 + +@end detailmenu +@end menu + +@node Introduction +@chapter Introduction + +각 노드는 목차에 정의된 메뉴 항목과 동일한 이름을 가져야 합니다. + +@node Formatting +@section Formatting +@c 내용 색인에 무언가를 추가하여 사람들이 검색할 때 +@c 여기에 올 수 있도록 합니다. +@cindex 굵은 텍스트 +@cindex 제목 + +장과 마찬가지로 섹션은 동일한 이름을 가져야 하며 동일한 순서로 나타나야 합니다. + +@subsection 이것은 하위 섹션 제목입니다 +@subsubsection 이것은 하위 하위 섹션 제목입니다 + +각 텍스트 블록은 단락입니다. 단락에 여러 줄을 사용할 수 있으며, +빈 줄만 단락을 구분합니다. + +일반적인 서식에는 @emph{강조}, @code{인라인 코드}가 포함됩니다. 특정 유형의 +텍스트도 표시할 수 있습니다: @file{file.txt}, @option{--learn-fast}, +@command{ls} 또는 @var{variable}. 다음과 같이 명령 문자를 이스케이프할 수 있습니다: +@@, 그리고 줄 끝에 단일 @@를 사용하여 줄 바꿈. + +다양한 유형의 블록을 추가할 수 있습니다: + +@example +다음은 예입니다. +@end example + +@lisp +'(이것은 lisp 코드입니다) +@end lisp + +@itemize +@item 정렬되지 않은 목록의 요소 +@item 동일한 목록의 두 번째 요소 +@end itemize + +@enumerate +@item 이 목록은 유사합니다 +@item 하지만 정렬됨 +@end enumerate + +@quotation +유명한 사람이 한 인용문 블록일 수 있습니다. +@end quotation + +@table @asis +@item 요소 제목 +요소 설명 + +@item 두 번째 요소 제목 +두 번째 요소 설명. 설명 부분은 여러 단락에 걸쳐 있을 수 있으며, +다른 블록 등을 포함할 수 있습니다. 이것은 일반적으로 정의 목록으로 사용됩니다. + +@code{@@asis}는 요소 제목을 감싸고 Texinfo에 그대로 사용하도록 지시합니다. +@end table + +@table @code +@item do-x +이 항목 제목은 이제 @code{@@code{do-x}}와 같이 코드 블록으로 래핑됩니다. +@end table + +@c 내용 색인은 문서의 어느 곳에나 나타날 수 있으며, 반드시 +@c 제목 뒤에 올 필요는 없습니다. +@cindex 함수 정의 +@deffn {함수의 종류} function_name @var{arg1} @var{arg2} @ + @var{arg3} @var{arg4} [@var{optional5}] +이 텍스트는 함수를 설명합니다. 단일 @@로 줄을 이스케이프하여 +함수 개요에 여러 줄을 사용할 수 있는 방법을 확인하십시오. + +이것은 다시 여러 단락이나 블록을 포함할 수 있습니다. +@end deffn + +@node Links +@section Links + +사용할 수 있는 다양한 유형의 링크가 있습니다. @uref{https://github.com}을 사용하여 +URL에 대한 간단한 링크를 만들고 선택적으로 제목을 추가할 수 있습니다: +@uref{https://github.com, GitHub}. 이메일 주소 @email{me@@me.me}. +이 문서의 노드, @xref{Introduction}. 항상 해당 노드의 정확한 이름을 +사용하십시오. @code{xref}는 링크 앞에 "see" 텍스트를 포함합니다. +다른 것을 삽입하려면 @pxref{Introduction}("See") 또는 +@xref{Introduction}(아무것도 삽입되지 않음)을 사용하십시오. 추가 인수를 사용하여 +링크 텍스트를 변경할 수 있습니다. @xref{Introduction, this introduction}. + +@code{@@xref{Node name,,, manual-name, link text}}와 같이 +더 많은 인수를 추가하여 이러한 명령으로 외부 매뉴얼에 연결할 수 있습니다. +Texinfo에 대한 전체 참조는 @xref{Overview,,, texinfo, Texinfo's manual}를 참조하십시오! + +@bye +``` + +## 사용 방법 + +`automake`를 사용하면 `Makefile.am`에 매뉴얼 경로를 +지정하기만 하면 됩니다: + +``` +info_TEXINFOS= doc/simple-manual.texi +``` + +그런 다음 `make doc/simple-manual.info`로 정보 매뉴얼을 얻거나 다른 형식으로, +예를 들어 `make doc/simple-manual.html`로 HTML을 얻을 수 있습니다. + +## 읽을거리 + +- [공식 매뉴얼](https://www.gnu.org/software/texinfo/manual/texinfo/html_node/) diff --git a/ko/textile.md b/ko/textile.md new file mode 100644 index 0000000000..2b455395c1 --- /dev/null +++ b/ko/textile.md @@ -0,0 +1,493 @@ +--- +name: Textile +contributors: + - ["Keith Miyake", "https://github.com/kaymmm"] +filename: learn-textile.textile +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Textile은 텍스트 서식 구문을 사용하여 일반 텍스트를 구조화된 HTML 마크업으로 변환하는 경량 마크업 언어입니다. 구문은 읽고 쓰기 쉽도록 설계된 HTML의 약식 버전입니다. Textile은 기사, 포럼 게시물, readme 문서 및 온라인에 게시되는 기타 모든 유형의 서면 콘텐츠를 작성하는 데 사용됩니다. + +- [주석](#comments) +- [단락](#paragraphs) +- [제목](#headings) +- [간단한 텍스트 스타일](#simple-text-styles) +- [목록](#lists) +- [코드 블록](#code-blocks) +- [가로줄](#horizontal-rule) +- [링크](#links) +- [이미지](#images) +- [각주 및 미주](#footnotes-and-endnotes) +- [표](#tables) +- [문자 변환](#character-conversions) +- [CSS](#css) +- [스팬 및 Div](#spans-and-divs) +- [추가 정보](#additional-info) + +## 주석 + +``` +###. 주석은 세(3) 개의 '#' 기호와 마침표 '.'로 시작합니다. +주석은 빈 줄이 나올 때까지 여러 줄에 걸쳐 있을 수 있습니다. + +###.. +여러 줄 주석(빈 줄 포함)은 세(3) 개의 '#' +기호와 두(2) 개의 마침표 '..'로 표시됩니다. + +이 줄도 위 주석의 일부입니다. + +주석은 다음 블록 요소가 나올 때까지 계속됩니다 + +p. 이 줄은 주석 처리되지 않았습니다 + + +``` + +## 단락 + +``` +###. 단락은 하나 이상의 빈 줄로 구분된 하나 또는 여러 개의 인접한 텍스트 줄입니다. 'p. '로 명시적으로 표시할 수도 있습니다. + +이것은 단락입니다. 단락에 입력하는 것이 재미있지 않나요? + +이제 단락 2에 있습니다. +저도 아직 단락 2에 있습니다! +빈 공간이 없는 줄 바꿈은 XHTML의
과 동일합니다. + +p. 저는 명시적으로 정의된 단락입니다 + + 빈 공간으로 시작하는 줄은

..

태그로 래핑되지 않습니다. + +###. 단락(및 모든 블록 요소)은 약식으로 정렬할 수 있습니다: + +p<. 왼쪽 정렬 단락(기본값). + +p>. 오른쪽 정렬 단락. + +p=. 가운데 정렬 단락. + +p<>. 양쪽 정렬 단락. + +h3>. 오른쪽 정렬

+ + +###. 단락은 각 em에 대해 괄호를 사용하여 들여쓸 수 있습니다. +들여쓰기는 padding-[left/right] css 스타일을 사용합니다. + +p(. 왼쪽 1em 들여쓰기. + +p((. 왼쪽 2em 들여쓰기. + +p))). 오른쪽 3em 들여쓰기. + +h2). 이것은

..

와 동일합니다. + + +###. 블록 인용은 'bq.' 태그를 사용합니다. + +bq. 이것은 블록 인용입니다. + +bq.:http://someurl.com '.' 바로 뒤에 인용 URL을 포함할 수 있습니다. + +bq.. 빈 줄을 포함하는 여러 줄 블록 인용은 +두 개의 마침표를 사용하여 표시됩니다 + +p. 여러 줄 블록 인용은 새 블록 요소가 나올 때까지 계속됩니다. + +bq. html을 사용하여 블록 인용에 바닥글을 추가할 수 있습니다: +
인용 텍스트
+ + +###. 서식이 미리 지정된 텍스트 블록: + +pre. 이 텍스트는 서식이 미리 지정되었습니다. <= 저 두 개의 공백은 그대로 유지됩니다. + +pre.. 이것은 여러 줄로 된 서식이 미리 지정된... + +…빈 줄을 포함하는 텍스트 블록입니다 + +p. 여러 줄로 된 서식이 미리 지정된 텍스트 블록을 새 블록 요소로 끝냅니다. +``` + +## 제목 + +

부터

까지의 HTML 요소를 해당 요소에 넣고 싶은 텍스트 앞에 'h#.'을 붙여 쉽게 만들 수 있습니다. 여기서 #은 1-6 수준입니다. +제목 뒤에는 빈 줄이 필요합니다. + + +``` +h1. 이것은

입니다 + +h2. 이것은

입니다 + +h3. 이것은

입니다 + +h4. 이것은

입니다 + +h5. 이것은

입니다 + +h6. 이것은
입니다 +``` + + +## 간단한 텍스트 스타일 + +``` +###. 굵은 텍스트와 강조 텍스트는 별표를 사용하여 표시됩니다: + +*이것은 강조 텍스트입니다* +**이것은 굵은 텍스트입니다** +이것은 단어 안의 [*굵은*] 텍스트입니다. + +*강조*와 **굵게**는 일반적으로 브라우저에서 동일하게 표시되지만 +다른 HTML 마크업을 사용하므로 구별됩니다. + +###. 이탤릭체 및 강조 텍스트는 밑줄을 사용하여 표시됩니다. + +_이것은 강조된 텍스트입니다_ +__이것은 이탤릭체 텍스트입니다__ +이것은 단어 안의 이탤[_릭_]체입니다. + +_강조된_ 텍스트와 __이탤릭체__ 텍스트는 일반적으로 브라우저에서 동일하게 표시되지만, +다시 말하지만, 다른 HTML 마크업을 사용하므로 구별됩니다. + +###. 위 첨자와 아래 첨자는 캐럿과 물결표를 사용합니다: + +위 첨자는 2^와^ 같고, 아래 첨자는 CO~2~L입니다. +위 첨자와 아래 첨자 주위의 공백에 유의하십시오. + +공백을 피하려면 대괄호를 사용하십시오: +2[^and^] 및 CO[~2~]L + +###. 삽입 및 삭제는 -/+ 기호로 표시됩니다: +이것은 -삭제된- 텍스트이고 이것은 +삽입된+ 텍스트입니다. + +###. 인용은 이중 '?'로 표시됩니다: + +??이것은 멋진 인용입니다?? +``` + +## 목록 + +``` +###. 순서 없는 목록은 별표 '*'를 사용하여 수준을 나타낼 수 있습니다: + +* 항목 +** 하위 항목 +* 다른 항목 +** 다른 하위 항목 +** 또 다른 하위 항목 +*** 3단계 깊이 + +###. 순서 있는 목록은 파운드 기호 '#'로 수행됩니다: + +# 항목 1 +# 항목 2 +## 항목 2-a +## 항목 2-b +# 항목 3 +** 순서 있는 목록 내의 혼합된 순서 없는 목록 + +###. 순서 있는 목록은 1 이상에서 시작할 수 있으며 다른 블록 뒤에 계속될 수 있습니다: + +#5 항목 5 +# 항목 6 + +추가 단락 + +#_ 위에서 계속되는 항목 7 +# 항목 8 + +###. 정의 목록은 대시와 할당으로 표시됩니다: + +- 첫 번째 항목 := 첫 번째 항목 정의 +- 두 번째 := 두 번째 정의 +- 여러 줄 := +여러 줄 +정의 =: +``` + +## 코드 블록 + +``` +코드 블록은 'bc.' 약식을 사용합니다: + +bc. 이것은 코드입니다 + 이것도 마찬가지입니다 + +이것은 코드 블록 밖에 있습니다 + +bc.. 이것은 여러 줄 코드 블록입니다 + +빈 줄은 여러 줄 코드 블록에 포함됩니다 + +p. 여러 줄 코드 블록을 모든 블록 요소로 끝냅니다 + +p. '@' 기호를 사용하여 @인라인 코드@를 표시합니다. +``` + +## 가로줄 + +가로줄(`
`)은 두 개의 하이픈으로 쉽게 추가할 수 있습니다. + +``` +-- +``` + +## 링크 + +``` +###. 링크 텍스트는 따옴표 안에 있고, 그 뒤에 콜론과 URL이 옵니다: + +"링크 텍스트":http://linkurl.com/ 일반 텍스트. + +"제목은 링크 텍스트 끝에 괄호 안에 들어갑니다"(mytitle):http://url.com +###. ...를 생성합니다 + +###. 링크 텍스트나 URL이 모호할 수 있을 때 대괄호를 사용하십시오: +["위키백과의 Textile":http://en.wikipedia.org/wiki/Textile_(markup_language)] + +###. 동일한 URL이 여러 번 참조될 경우 명명된 링크가 유용합니다. +"txstyle":txstyle 웹사이트에 대한 여러 "참조":txstyle. + +[txstyle]https://txstyle.org/ +``` + +## 이미지 + +``` +###. 이미지는 URL을 느낌표(!)로 둘러싸서 포함할 수 있습니다. +대체 텍스트는 URL 뒤에 괄호 안에 포함되며 링크할 수도 있습니다: + +!http://imageurl.com! + +!http://imageurl.com(이미지 대체 텍스트)! + +!http://imageurl.com(대체 텍스트)!:http://image-link-url.com +``` + +## 각주 및 미주 + +``` +각주는 대괄호 안에 참조 ID로 표시됩니다.[1] + +fn1. "링크":http://link.com가 있는 각주 텍스트. + +링크가 없는 각주.[2!] + +fn2. 해당 링크 없는 각주. + +각주에서 텍스트로 다시 연결되는 백링크가 있는 각주.[3] + +fn3^. 이 각주는 텍스트 내 인용으로 다시 연결됩니다. + + +미주는 자동으로 번호가 매겨지고[#first] 대괄호와 키 값[#second]으로 표시됩니다[#first]. 링크되지 않을 수도 있습니다[#unlinkednote!] + +###. 미주 텍스트 제공: + +note#first. 이것은 첫 번째 미주 텍스트입니다. + +note#second. 이것은 두 번째 텍스트입니다. + +note#unlinkednote. 이것은 텍스트에서 링크되지 않았습니다. + +### notelist 블록을 사용하여 텍스트에 노트 목록을 배치합니다: +이 목록은 #1부터 시작합니다. 알파벳이나 그리스 문자를 사용할 수도 있습니다. +notelist:1. ###. 1에서 시작 (그런 다음 2, 3, 4...) +notelist:c. ###. c에서 시작 (그런 다음 d, e, f...) +notelist:α. ###. α에서 시작 (그런 다음 β, γ, δ...) + +###. notelist 구문은 다음과 같습니다: + +notelist. 모든 인용에 대한 백링크가 있는 노트. +notelist+. 모든 인용에 대한 백링크가 있는 노트, + 그 뒤에 참조되지 않은 노트가 옵니다. +notelist^. 각 노트에 대한 첫 번째 인용에 대한 백링크가 하나 있는 노트. +notelist^+. 각 노트에 대한 첫 번째 인용에 대한 백링크가 하나 있는 노트, + 그 뒤에 참조되지 않은 노트가 옵니다. +notelist!. 인용에 대한 백링크가 없는 노트. +notelist!+. 인용에 대한 백링크가 없는 노트, 그 뒤에 + 참조되지 않은 노트가 옵니다. +``` + +## 표 + + +``` +###. 표는 파이프 '|' 기호를 사용하여 간단하게 정의됩니다. + +| A | 간단한 | 표 | 행 | +| 그리고 | 다른 | 표 | 행 | +| 빈 | | 셀 | 이 있는 | + +###. 헤더는 '|_. '가 앞에 옵니다. +|_. 첫 번째 헤더 |_. 두 번째 헤더 | +| 내용 셀 | 내용 셀 | + +###. 태그는 제목 위 |^.와 아래 |-.가 사용될 때 추가됩니다. + +|^. +|_. 첫 번째 헤더 |_. 두 번째 헤더 | +|-. +| 내용 셀 | 내용 셀 | +| 내용 셀 | 내용 셀 | + +###. 태그는 바닥글 위 |~.와 아래 |-.가 사용될 때 추가됩니다. + +|~. +|\2=. 두 열에 걸쳐 가운데 정렬된 바닥글 | +|-. +| 내용 셀 | 내용 셀 | +| 내용 셀 | 내용 셀 | + +###. 속성은 개별 셀, 행 또는 전체 표에 적용할 수 있습니다. +셀 속성은 각 셀 안에 배치됩니다: + +|a|{color:red}. 스타일이 적용된|셀| + +###. 행 속성은 행 시작 부분에 배치되고, +그 뒤에 점과 공백이 옵니다: + +(rowclass). |a|classy|row| + +###. 표 속성은 표 바로 앞에 특수 'table.' 블록 +수정자를 배치하여 지정됩니다: + +table(tableclass). +|a|classy|table| +|a|classy|table| + +###. 행과 열 병합: +백슬래시 \는 열 병합에 사용됩니다: + +|\2. 두 열 병합 | +| 열 1 | 열 2 | + +###. 슬래시 /는 행 병합에 사용됩니다: + +|/3. 3개 행 병합 | 행 a | +| 행 b | +| 행 c | + +###. 표 셀 내의 수직 정렬: + +|^. 위쪽 정렬| +|-. 가운데 정렬| +|~. 아래쪽 정렬| + +###. 표 셀 내의 수평 정렬 + +|:\1. |400| +|=. 가운데 정렬 | +| 정렬 없음 | +|>. 오른쪽 정렬 | +``` + +또는 동일한 결과에 대해 + +``` +열 1 | 열 2 | 열 3 +:-- | :-: | --: +으악 너무 못생겼어 | 멈춰 | 줘 +``` + + +## 문자 변환 + +### 등록 상표, 상표, 저작권 기호 + +``` +등록 상표(r), 상표(tm), 저작권 (c) +``` + +### 약어 + +``` +###. 약어 정의는 괄호 안에 제공될 수 있습니다: + +EPA(환경 보호국) 및 CDC(질병 통제 예방 센터) +``` + +### 꺾쇠괄호 및 앰퍼샌드 + +``` +### 꺾쇠괄호 < 및 >와 앰퍼샌드 &는 자동으로 이스케이프됩니다: +< => < +> => > +& => & +``` + +### 생략 부호 + +``` +p. 세 개의 연속된 마침표는 생략 부호로 자동 변환됩니다... +``` + +### Em 및 En 대시 + +``` +###. En 대시(짧음)는 공백으로 둘러싸인 하이픈입니다: + +이 줄은 2018년 10월 - 11월을 구분하기 위해 en 대시를 사용합니다. + +###. Em 대시(길음)는 공백이 있거나 없는 두 개의 하이픈입니다: + +이것은 절을 구분하는 데 사용되는 em 대시입니다--. +그러나 공백과 함께 사용할 수도 있습니다 -- 덜 사용되는 관례입니다. +'less'와 'used' 사이의 마지막 하이픈은 단어 사이에서 변환되지 않습니다. +``` + +## 분수 및 기타 수학 기호 + +``` +4분의 1: (1/4) => ¼ +2분의 1: (1/2) => ½ +4분의 3: (3/4) => ¾ +도: (o) => ° +플러스/마이너스: (+/-) => ± +``` + +### 곱셈/치수 + +``` +p. 문자 'x'로 구분된 숫자는 곱셈 +또는 치수 기호 '×'로 변환됩니다: +3 x 5 => 3 × 5 +``` + +### 따옴표 및 아포스트로피 + +``` +###. 직선 따옴표와 아포스트로피는 자동으로 +해당하는 곱슬 형태로 변환됩니다: + +"이것들", '이것들', 그리고 this'n은 해당 HTML 엔티티로 변환됩니다. +텍스트 주위에 '=='를 사용하여 직선으로 유지하십시오: =="직선 따옴표"==. +``` + +## CSS + +``` +p{color:blue}. CSS 스타일은 중괄호 '{}'로 묶습니다. +p(my-class). 클래스는 괄호로 묶습니다. +p(#my-id). ID는 괄호로 묶고 파운드 '#' 기호를 앞에 붙입니다. +``` + +## 스팬 및 Div + +``` +%스팬%은 퍼센트 기호로 묶습니다. +div. Div는 'div.' 약식으로 표시됩니다. +``` + +--- + +## 추가 정보 + +* TxStyle Textile 문서: [https://txstyle.org/](https://txstyle.org/) +* promptworks Textile 참조 매뉴얼: [https://www.promptworks.com/textile](https://www.promptworks.com/textile) +* Redmine Textile 서식: [http://www.redmine.org/projects/redmine/wiki/RedmineTextFormattingTextile](http://www.redmine.org/projects/redmine/wiki/RedmineTextFormattingTextile) diff --git a/ko/tmux.md b/ko/tmux.md new file mode 100644 index 0000000000..2d800a8e7a --- /dev/null +++ b/ko/tmux.md @@ -0,0 +1,244 @@ +--- +category: tool +name: tmux +contributors: + - ["mdln", "https://github.com/mdln"] +filename: LearnTmux.txt +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +[tmux](http://tmux.github.io)는 터미널 멀티플렉서입니다: 여러 터미널을 +하나의 화면에서 생성, 액세스 및 제어할 수 있습니다. tmux는 +화면에서 분리되어 백그라운드에서 계속 실행될 수 있으며, +나중에 다시 연결할 수 있습니다. + + +``` + tmux [command] # 명령어 실행 + # 명령어가 없는 'tmux'는 새 세션을 생성합니다. + + new # 새 세션 생성 + -s "Session" # 명명된 세션 생성 + -n "Window" # 명명된 창 생성 + -c "/dir" # 대상 디렉토리에서 시작 + + attach # 마지막/사용 가능한 세션에 연결 + -t "#" # 대상 세션에 연결 + -d # 다른 인스턴스에서 세션 분리 + + ls # 열린 세션 목록 + -a # 모든 열린 세션 목록 + + lsw # 창 목록 + -a # 모든 창 목록 + -s # 세션의 모든 창 목록 + + lsp # 창 목록 + -a # 모든 창 목록 + -s # 세션의 모든 창 목록 + -t # 대상의 모든 창 목록 + + kill-window # 현재 창 종료 + -t "#" # 대상 창 종료 + -a # 모든 창 종료 + -a -t "#" # 대상을 제외한 모든 창 종료 + + kill-session # 현재 세션 종료 + -t "#" # 대상 세션 종료 + -a # 모든 세션 종료 + -a -t "#" # 대상을 제외한 모든 세션 종료 +``` + + +### 키 바인딩 + +연결된 tmux 세션을 제어하는 방법은 '접두사' 키라고 하는 +키 조합을 통하는 것입니다. + +``` +---------------------------------------------------------------------- + (C-b) = Ctrl + b # 키 바인딩을 사용하려면 '접두사' 조합이 필요합니다. + + (M-1) = Meta + 1 -또는- Alt + 1 +---------------------------------------------------------------------- + + ? # 모든 키 바인딩 목록 + : # tmux 명령어 프롬프트 입력 + r # 연결된 클라이언트 강제 다시 그리기 + c # 새 창 생성 + + ! # 현재 창을 창에서 분리합니다. + % # 현재 창을 좌우로 분할합니다. + " # 현재 창을 위아래로 분할합니다. + + n # 다음 창으로 변경 + p # 이전 창으로 변경 + { # 현재 창을 이전 창과 교체 + } # 현재 창을 다음 창과 교체 + [ # 텍스트 복사 또는 기록 보기를 위해 복사 모드로 들어갑니다. + + s # 연결된 클라이언트에 대한 새 세션을 대화식으로 + # 선택합니다. + w # 현재 창을 대화식으로 선택 + 0 to 9 # 창 0에서 9까지 선택 + + d # 현재 클라이언트 분리 + D # 분리할 클라이언트 선택 + + & # 현재 창 종료 + x # 현재 창 종료 + + Up, Down # 위, 아래, 왼쪽 또는 오른쪽 창으로 변경 + Left, Right + + M-1 to M-5 # 창 정렬: + # 1) 수평으로 균등하게 + # 2) 수직으로 균등하게 + # 3) 주 수평 + # 4) 주 수직 + # 5) 타일형 + + C-Up, C-Down # 현재 창 크기를 한 셀 단위로 조정 + C-Left, C-Right + + M-Up, M-Down # 현재 창 크기를 다섯 셀 단위로 조정 + M-Left, M-Right +``` + + +### ~/.tmux.conf 구성 + +tmux.conf는 .vimrc나 init.el이 사용되는 것처럼 시작 시 +옵션을 자동으로 설정하는 데 사용할 수 있습니다. + +``` +# 예제 tmux.conf +# 2015.12 + + +### 일반 +########################################################################### + +# 스크롤백/기록 제한 +set -g history-limit 2048 + +# 인덱스 시작 +set -g base-index 1 + +# 마우스 +set-option -g -q mouse on + +# 구성 파일 강제 다시 로드 +unbind r +bind r source-file ~/.tmux.conf + + +### 키 바인딩 +########################################################################### + +# 기본 접두사로 C-b 바인딩 해제 +unbind C-b + +# 새 기본 접두사 설정 +set-option -g prefix ` + +# 접두사를 두 번 누를 때 이전 창으로 돌아가기 +bind C-a last-window +bind ` last-window + +# F11/F12를 사용하여 C-a와 ` 교체 허용 +bind F11 set-option -g prefix C-a +bind F12 set-option -g prefix ` + +# 키 바인딩 기본 설정 +setw -g mode-keys vi +set-option -g status-keys vi + +# vim 이동 키로 창 간 이동 +bind h select-pane -L +bind j select-pane -D +bind k select-pane -U +bind l select-pane -R + +# 창 순환/교체 +bind e previous-window +bind f next-window +bind E swap-window -t -1 +bind F swap-window -t +1 + +# 쉬운 창 분할 명령어 +bind = split-window -h +bind - split-window -v +unbind '"' +unbind % + +# 명령을 보내기 위해 가장 안쪽 세션 활성화 (tmux 중첩 시) +bind a send-prefix + + +### 테마 +########################################################################### + +# 상태 표시줄 색상 팔레트 +set-option -g status-justify left +set-option -g status-bg black +set-option -g status-fg white +set-option -g status-left-length 40 +set-option -g status-right-length 80 + +# 창 테두리 색상 팔레트 +set-option -g pane-active-border-fg green +set-option -g pane-active-border-bg black +set-option -g pane-border-fg white +set-option -g pane-border-bg black + +# 메시지 색상 팔레트 +set-option -g message-fg black +set-option -g message-bg green + +# 창 상태 색상 팔레트 +setw -g window-status-bg black +setw -g window-status-current-fg green +setw -g window-status-bell-attr default +setw -g window-status-bell-fg red +setw -g window-status-activity-attr default +setw -g window-status-activity-fg yellow + + +### UI +########################################################################### + +# 알림 +setw -g monitor-activity on +set -g visual-activity on +set-option -g bell-action any +set-option -g visual-bell off + +# 창 제목 자동 설정 +set-option -g set-titles on +set-option -g set-titles-string '#H:#S.#I.#P #W #T' # 창 번호, 프로그램 이름, 활성(또는 비활성) + +# 상태 표시줄 조정 +set -g status-left "#[fg=red] #H#[fg=green]:#[fg=white]#S#[fg=green] |#[default]" + +# 상태 표시줄에 성능 카운터 표시 +# https://github.com/thewtex/tmux-mem-cpu-load/ 필요 +set -g status-interval 4 +set -g status-right "#[fg=green] | #[fg=white]#(tmux-mem-cpu-load)#[fg=green] | #[fg=cyan]%H:%M #[default]" +``` + + +### 참고 자료 + +[Tmux | 홈페이지](http://tmux.github.io) + +[Tmux 매뉴얼 페이지](http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man1/tmux.1?query=tmux) + +[Gentoo 위키](http://wiki.gentoo.org/wiki/Tmux) + +[Archlinux 위키](https://wiki.archlinux.org/index.php/Tmux) + +[상태 표시줄에 CPU/MEM % 표시](https://stackoverflow.com/questions/11558907/is-there-a-better-way-to-display-cpu-usage-in-tmux) + +[tmuxinator - 복잡한 tmux 세션 관리](https://github.com/tmuxinator/tmuxinator) diff --git a/ko/toml.md b/ko/toml.md new file mode 100644 index 0000000000..9893d8ba9e --- /dev/null +++ b/ko/toml.md @@ -0,0 +1,311 @@ +--- +name: TOML +filename: learntoml.toml +contributors: + - ["Alois de Gouvello", "https://github.com/aloisdg"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +TOML은 Tom's Obvious, Minimal Language의 약자입니다. 명백한 의미 체계로 인해 읽기 쉬운 최소한의 구성 파일 형식을 목표로 설계된 데이터 직렬화 언어입니다. + +YAML 및 JSON의 대안입니다. JSON보다 인간 친화적이고 YAML보다 간단한 것을 목표로 합니다. TOML은 해시 테이블에 명확하게 매핑되도록 설계되었습니다. TOML은 다양한 언어의 데이터 구조로 쉽게 구문 분석할 수 있어야 합니다. + +이 문서는 [TOML v1.0.0](https://toml.io/en/v1.0.0)을 따릅니다. 향후 [변경 사항](https://github.com/toml-lang/toml/blob/main/CHANGELOG.md)은 사소하고 이전 버전과 호환될 것으로 예상됩니다. + +```toml +# TOML의 주석은 이와 같습니다. + +################ +# 스칼라 유형 # +################ + +# 루트 객체(문서 전체에 걸쳐 계속됨)는 다른 언어의 사전, 해시 또는 객체와 +# 동일한 맵입니다. + +# 키, 등호 및 값은 같은 줄에 있어야 합니다. +# (일부 값은 여러 줄로 나눌 수 있음). +key = "value" +string = "hello" +number = 42 +float = 3.14 +boolean = true +dateTime = 1979-05-27T07:32:00-08:00 +scientificNotation = 1e+12 +"key can be quoted" = true # "와 ' 모두 괜찮습니다 +"unquoted key may contain" = "letters, numbers, underscores, and dashes" +other_kêys = "are permitted by spec but most implementations don't actually permit them" + +# 따옴표 없는 키는 비어 있으면 안 되지만, 빈 따옴표 키는 허용됩니다. +"" = "blank" # 유효하지만 권장하지 않음 +'' = 'blank' # 유효하지만 권장하지 않음 + +########## +# 문자열 # +########## + +# 모든 문자열은 유효한 UTF-8 문자만 포함해야 합니다. +# 문자를 이스케이프할 수 있으며 일부는 간결한 이스케이프 시퀀스를 가집니다. +# 예를 들어, \t는 탭을 추가합니다. 모든 것을 얻으려면 사양을 참조하십시오. +basicString = "are surrounded by quotation marks. \"I'm quotable\". Name\tJos" + +multiLineString = """ +는 각 측면에 세 개의 따옴표로 둘러싸여 있으며 +개행을 허용합니다.""" + +literalString = '는 작은따옴표로 둘러싸여 있습니다. 이스케이프는 허용되지 않습니다.' + +multiLineLiteralString = ''' +는 각 측면에 세 개의 작은따옴표로 둘러싸여 있으며 +개행을 허용합니다. 여전히 이스케이프는 없습니다. +첫 번째 개행은 원시 문자열에서 잘립니다. + 다른 모든 공백은 + 보존됩니다. #!는 보존됩니까? +''' + +# 이진 데이터의 경우 Base64, 다른 ASCII 또는 UTF8 +# 인코딩을 사용하는 것이 좋습니다. 해당 인코딩의 처리는 +# 응용 프로그램에 따라 다릅니다. + +########### +# 정수 # +########### + +## 정수는 +, - 또는 아무것도 없이 시작할 수 있습니다. +## 선행 0은 허용되지 않습니다. +## 16진수, 8진수 및 2진수 형식이 허용됩니다. +## 일련의 숫자로 표현할 수 없는 값은 허용되지 않습니다. +int1 = +42 +int2 = 0 +int3 = -21 +int4 = 0xdeadbeef +int5 = 0o755 +int6 = 0b11011100 +integerRange = 64 + +## 가독성을 높이기 위해 밑줄을 사용할 수 있습니다. 각 +## 밑줄은 적어도 하나의 숫자로 둘러싸여 있어야 합니다. +int4 = 5_349_221 +int5 = 1_2_3_4_5 # 유효하지만 권장하지 않음 + +######### +# 부동 소수점 # +######### + +# 부동 소수점은 정수 뒤에 소수 및/또는 지수 부분이 옵니다. +flt1 = 3.1415 +flt2 = -5e6 +flt3 = 6.626E-34 + +########### +# 부울 # +########### + +bool1 = true +bool2 = false +boolMustBeLowercase = true + +############ +# 날짜/시간 # +############ + +date1 = 1979-05-27T07:32:00Z # UTC 시간, RFC 3339/ISO 8601 사양 따름 +date2 = 1979-05-26T15:32:00+08:00 # RFC 3339/ISO 8601 오프셋 포함 +date3 = 1979-05-27T07:32:00 # 오프셋 없음 +date4 = 1979-05-27 # 오프셋 또는 시간 없음 + +#################### +# 컬렉션 유형 # +#################### + +######### +# 배열 # +######### + +array1 = [ 1, 2, 3 ] +array2 = [ "쉼표", "는", "구분 기호" ] +array3 = [ "다른", "유형을", "혼합하지 마십시오" ] +array4 = [ [ 1.2, 2.4 ], ["모든", '문자열은', """같은""", '''유형입니다'''] ] +array5 = [ + "공백", "은", "무시됩니다" +] + +######### +# 테이블 # +######### + +# 테이블(또는 해시 테이블 또는 사전)은 키/값 쌍의 컬렉션입니다. +# 한 줄에 대괄호로 표시됩니다. +# 빈 테이블은 허용되며 키/값 쌍이 없습니다. +[table] + +# 그 아래, 그리고 다음 테이블이나 EOF까지는 해당 테이블의 키/값입니다. +# 테이블 내의 키/값 쌍은 특정 순서로 보장되지 않습니다. +[table-1] +key1 = "some string" +key2 = 123 + +[table-2] +key1 = "another string" +key2 = 456 + +# 점은 중첩 테이블을 나타내는 데 사용되므로 따옴표 없는 키에는 점이 금지됩니다. +# 각 점으로 구분된 부분의 명명 규칙은 키와 동일합니다. +[dog."tater.man"] +type = "pug" + +# JSON 세계에서는 다음과 같은 구조를 제공합니다: +# { "dog": { "tater.man": { "type": "pug" } } } + +# 점으로 구분된 부분 주위의 공백은 무시되지만, 모범 사례는 +# 불필요한 공백을 사용하지 않는 것입니다. +[a.b.c] # 이것이 모범 사례입니다 +[ d.e.f ] # [d.e.f]와 동일 +[ j . "ʞ" . 'l' ] # [j."ʞ".'l']와 동일 + +# 원하지 않으면 모든 상위 테이블을 지정할 필요가 없습니다. TOML은 +# 당신을 위해 그것을 하는 방법을 알고 있습니다. +# [x] 당신은 +# [x.y] 이것들을 +# [x.y.z] 필요하지 않습니다 +[x.y.z.w] # 이것이 작동하도록 + +# 상위 테이블이 직접 정의되지 않았고 특정 키를 정의하지 않은 한, +# 여전히 쓸 수 있습니다. +[a.b] +c = 1 + +[a] +d = 2 + +# JSON에서 다음을 생성합니다: +# { "a": {"b": {"c": 1}, "d": 2 } } + +# 키나 테이블을 두 번 이상 정의할 수 없습니다. 그렇게 하는 것은 유효하지 않습니다. + +# 이것을 하지 마십시오 +[a] +b = 1 + +[a] +c = 2 + +# 이것도 하지 마십시오 +[a] +b = 1 + +[a.b] +c = 2 + +# 모든 테이블 이름은 비어 있으면 안 됩니다. +[] # 유효하지 않음 +[a.] # 유효하지 않음 +[a..b] # 유효하지 않음 +[.b] # 유효하지 않음 +[.] # 유효하지 않음 + +################ +# 인라인 테이블 # +################ + +inlineTables = { areEnclosedWith = "{ and }", a = { b = { c = { d = 1 } } } } +point = { x = 1, y = 2 } +usingMultiple = { + lines = "discouraged!", + instead = "use normal TOML tables", +} + +################### +# 테이블 배열 # +################### + +# 테이블 배열은 이중 대괄호로 테이블 이름을 사용하여 표현할 수 있습니다. +# 동일한 이중 대괄호 이름의 각 테이블은 배열의 항목이 됩니다. +# 테이블은 마주치는 순서대로 삽입됩니다. + +[[products]] +name = "array of table" +sku = 738594937 +emptyTableAreAllowed = true + +[[products]] + +[[products]] +name = "Nail" +sku = 284758393 +color = "gray" +``` + +JSON의 동등한 것은 다음과 같습니다: + +```json +{ + "products": [ + { + "name": "array of table", + "sku": 7385594937, + "emptyTableAreAllowed": true + }, + {}, + { + "name": "Nail", + "sku": 284758393, + "color": "gray" + } + ] +} +``` + +```toml +# 중첩된 테이블 배열도 만들 수 있습니다. 각 이중 대괄호로 묶인 +# 하위 테이블은 그 위의 가장 가까운 테이블 요소에 속합니다. + +[[fruit]] + name = "apple" # 나는 과일 테이블/맵의 속성입니다 + + [fruit.geometry] + shape = "round" + note = "나는 기하학 테이블/맵의 속성입니다" + + [[fruit.color]] + name = "red" + note = "나는 사과 과일의 테이블/맵에 있는 배열 항목입니다" + + [[fruit.color]] + name = "green" + note = "나는 빨간색과 같은 배열에 있습니다" + +[[fruit]] + name = "banana" + + [[fruit.color]] + name = "yellow" + note = "나는 바나나 과일의 테이블/맵에 있는 배열 항목입니다" +``` + +JSON의 동등한 것은 다음과 같습니다: + +``` +{ + "fruit": [ + { + "name": "apple", + "geometry": { "shape": "round", "note": "..."}, + "color": [ + { "name": "red", "note": "..." }, + { "name": "green", "note": "..." } + ] + }, + { + "name": "banana", + "color": [ + { "name": "yellow", "note": "..." } + ] + } + ] +} +``` + +### 더 많은 자료 + ++ [TOML 공식 저장소](https://github.com/toml-lang/toml) diff --git a/ko/typescript.md b/ko/typescript.md new file mode 100644 index 0000000000..d3506d98cf --- /dev/null +++ b/ko/typescript.md @@ -0,0 +1,294 @@ +--- +name: TypeScript +contributors: + - ["Philippe Vlérick", "https://github.com/pvlerick"] + - ["Kiwimoe", "https://github.com/kiwimoe"] +filename: learntypescript.ts +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +TypeScript는 JavaScript로 작성된 대규모 애플리케이션 개발을 용이하게 하는 것을 목표로 하는 언어입니다. TypeScript는 클래스, 모듈, 인터페이스, 제네릭 및 (선택적) 정적 타이핑과 같은 일반적인 개념을 JavaScript에 추가합니다. JavaScript의 상위 집합입니다. 모든 JavaScript 코드는 유효한 TypeScript 코드이므로 모든 프로젝트에 원활하게 추가할 수 있습니다. TypeScript 컴파일러는 JavaScript를 내보냅니다. + +이 기사는 [JavaScript](../javascript/)와 달리 TypeScript 추가 구문에만 초점을 맞춥니다. + +TypeScript 컴파일러를 테스트하려면 [Playground](https://www.typescriptlang.org/play)로 이동하여 코드를 입력하고 자동 완성을 사용하며 내보낸 JavaScript를 직접 볼 수 있습니다. + +```ts +// TypeScript에는 3가지 기본 유형이 있습니다. +let isDone: boolean = false; +let lines: number = 42; +let name: string = "Anders"; + +// 그러나 변수가 명시적 리터럴에서 파생된 경우 유형 주석을 생략할 수 있습니다. +let isDone = false; +let lines = 42; +let name = "Anders"; + +// 알 수 없는 경우 "Any" 유형이 있습니다. +let notSure: any = 4; +notSure = "maybe a string instead"; +notSure = false; // 좋아요, 확실히 부울입니다. + +// 상수에 const 키워드 사용 +const numLivesForCat = 9; +numLivesForCat = 1; // 오류 + +// 컬렉션의 경우 유형이 지정된 배열과 제네릭 배열이 있습니다. +let list: number[] = [1, 2, 3]; +// 또는 제네릭 배열 유형 사용 +let list: Array = [1, 2, 3]; + +// 열거형의 경우: +enum Color { Red, Green, Blue }; +let c: Color = Color.Green; +console.log(Color[c]); // "Green" + +// 마지막으로 "void"는 함수가 아무것도 반환하지 않는 특수한 경우에 사용됩니다. +function bigHorribleAlert(): void { + alert("I'm a little annoying box!"); +} + +// 함수는 일급 시민이며 람다 "뚱뚱한 화살표" 구문을 지원하고 +// 유형 추론을 사용합니다. + +// 다음은 동일하며 컴파일러에서 동일한 서명이 추론되고 +// 동일한 JavaScript가 내보내집니다. +let f1 = function (i: number): number { return i * i; } +// 반환 유형 추론됨 +let f2 = function (i: number) { return i * i; } +// "뚱뚱한 화살표" 구문 +let f3 = (i: number): number => { return i * i; } +// 반환 유형이 추론된 "뚱뚱한 화살표" 구문 +let f4 = (i: number) => { return i * i; } +// 반환 유형이 추론된 "뚱뚱한 화살표" 구문, 중괄호 없음은 반환 +// 키워드가 필요 없음을 의미합니다. +let f5 = (i: number) => i * i; + +// 함수는 둘 이상의 유형을 허용할 수 있습니다. +function f6(i: string | number): void { + console.log("The value was " + i); +} + +// 인터페이스는 구조적이며 속성이 있는 모든 것은 +// 인터페이스와 호환됩니다. +interface Person { + name: string; + // 선택적 속성, "?"로 표시 + age?: number; + // 그리고 물론 함수 + move(): void; +} + +// "Person" 인터페이스를 구현하는 객체 +// 이름과 이동 속성이 있으므로 Person으로 취급할 수 있습니다. +let p: Person = { name: "Bobby", move: () => { } }; +// 선택적 속성이 있는 객체: +let validPerson: Person = { name: "Bobby", age: 42, move: () => { } }; +// 나이가 숫자가 아니므로 사람이 아닙니다. +let invalidPerson: Person = { name: "Bobby", age: true }; + +// 인터페이스는 함수 유형을 설명할 수도 있습니다. +interface SearchFunc { + (source: string, subString: string): boolean; +} +// 매개변수의 유형만 중요하며 이름은 중요하지 않습니다. +let mySearch: SearchFunc; +mySearch = function (src: string, sub: string) { + return src.search(sub) != -1; +} + +// 클래스 - 멤버는 기본적으로 public입니다. +class Point { + // 속성 + x: number; + + // 생성자 - 이 컨텍스트의 public/private 키워드는 + // 속성 및 생성자의 초기화에 대한 상용구 코드를 + // 생성합니다. + // 이 예에서 "y"는 "x"와 동일하게 정의되지만 코드가 더 적습니다. + // 기본값도 지원됩니다. + + constructor(x: number, public y: number = 0) { + this.x = x; + } + + // 함수 + dist(): number { return Math.sqrt(this.x * this.x + this.y * this.y); } + + // 정적 멤버 + static origin = new Point(0, 0); +} + +// 클래스는 인터페이스를 구현하는 것으로 명시적으로 표시할 수 있습니다. +// 누락된 속성은 컴파일 타임에 오류를 발생시킵니다. +class PointPerson implements Person { + name: string + move() {} +} + +let p1 = new Point(10, 20); +let p2 = new Point(25); //y는 0이 됩니다. + +// 상속 +class Point3D extends Point { + constructor(x: number, y: number, public z: number = 0) { + super(x, y); // 슈퍼 클래스 생성자를 명시적으로 호출해야 합니다. + } + + // 재정의 + dist(): number { + let d = super.dist(); + return Math.sqrt(d * d + this.z * this.z); + } +} + +// 모듈, "."는 하위 모듈의 구분 기호로 사용할 수 있습니다. +module Geometry { + export class Square { + constructor(public sideLength: number = 0) { + } + area() { + return Math.pow(this.sideLength, 2); + } + } +} + +let s1 = new Geometry.Square(5); + +// 모듈을 참조하기 위한 로컬 별칭 +import G = Geometry; + +let s2 = new G.Square(10); + +// 제네릭 +// 클래스 +class Tuple { + constructor(public item1: T1, public item2: T2) { + } +} + +// 인터페이스 +interface Pair { + item1: T; + item2: T; +} + +// 및 함수 +let pairToTuple = function (p: Pair) { + return new Tuple(p.item1, p.item2); +}; + +let tuple = pairToTuple({ item1: "hello", item2: "world" }); + +// 정의 파일에 대한 참조 포함: +/// + +// 템플릿 문자열 (백틱을 사용하는 문자열) +// 템플릿 문자열을 사용한 문자열 보간 +let name = 'Tyrone'; +let greeting = `Hi ${name}, how are you?` +// 템플릿 문자열을 사용한 여러 줄 문자열 +let multiline = `This is an example +of a multiline string`; + +// READONLY: TypeScript 3.1의 새로운 기능 +interface Person { + readonly name: string; + readonly age: number; +} + +var p1: Person = { name: "Tyrone", age: 42 }; +p1.age = 25; // 오류, p1.age는 읽기 전용입니다. + +var p2 = { name: "John", age: 60 }; +var p3: Person = p2; // 확인, p2에 대한 읽기 전용 별칭 +p3.age = 35; // 오류, p3.age는 읽기 전용입니다. +p2.age = 45; // 확인, 하지만 별칭 때문에 p3.age도 변경됩니다. + +class Car { + readonly make: string; + readonly model: string; + readonly year = 2018; + + constructor() { + this.make = "Unknown Make"; // 생성자에서 할당 허용 + this.model = "Unknown Model"; // 생성자에서 할당 허용 + } +} + +let numbers: Array = [0, 1, 2, 3, 4]; +let moreNumbers: ReadonlyArray = numbers; +moreNumbers[5] = 5; // 오류, 요소는 읽기 전용입니다. +moreNumbers.push(5); // 오류, push 메서드 없음 (배열을 변경하기 때문) +moreNumbers.length = 3; // 오류, 길이는 읽기 전용입니다. +numbers = moreNumbers; // 오류, 변경 메서드가 없습니다. + +// 여러 모양 중 하나일 수 있는 상태를 모델링하기 위한 태그된 유니온 타입 +type State = + | { type: "loading" } + | { type: "success", value: number } + | { type: "error", message: string }; + +declare const state: State; +if (state.type === "success") { + console.log(state.value); +} else if (state.type === "error") { + console.error(state.message); +} + +// 템플릿 리터럴 타입 +// 복잡한 문자열 타입을 만드는 데 사용 +type OrderSize = "regular" | "large"; +type OrderItem = "Espresso" | "Cappuccino"; +type Order = `A ${OrderSize} ${OrderItem}`; + +let order1: Order = "A regular Cappuccino"; +let order2: Order = "A large Espresso"; +let order3: Order = "A small Espresso"; // 오류 + +// 반복자 및 생성기 + +// for..of 문 +// 반복되는 객체의 값 목록을 반복 +let arrayOfAnyType = [1, "string", false]; +for (const val of arrayOfAnyType) { + console.log(val); // 1, "string", false +} + +let list = [4, 5, 6]; +for (const i of list) { + console.log(i); // 4, 5, 6 +} + +// for..in 문 +// 반복되는 객체의 키 목록을 반복 +for (const i in list) { + console.log(i); // "0", "1", "2" +} + +// 타입 단언 + +let foo = {} // foo를 빈 객체로 생성 +foo.bar = 123 // 오류: 속성 'bar'가 `{}`에 존재하지 않음 +foo.baz = 'hello world' // 오류: 속성 'baz'가 `{}`에 존재하지 않음 + +// foo의 추론된 타입이 `{}`(속성 0개인 객체)이므로 +// bar와 baz를 추가할 수 없습니다. 그러나 타입 단언을 사용하면 +// 다음이 통과합니다. + +interface Foo { + bar: number; + baz: string; +} + +let foo = {} as Foo; // 여기서 타입 단언 +foo.bar = 123; +foo.baz = 'hello world' +``` + +## 더 읽을거리 + +* [공식 TypeScript 웹사이트](https://www.typescriptlang.org/) +* [GitHub의 소스 코드](https://github.com/microsoft/TypeScript) +* [TypeScript 배우기](https://learntypescript.dev/) diff --git a/ko/uxntal.md b/ko/uxntal.md new file mode 100644 index 0000000000..82b243bcfb --- /dev/null +++ b/ko/uxntal.md @@ -0,0 +1,175 @@ +--- +name: Uxntal +contributors: + - ["Devine Lu Linvega", "https://wiki.xxiivv.com"] +filename: learnuxn.tal +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Uxntal은 [Uxn 가상 머신](https://wiki.xxiivv.com/site/uxn.html)을 대상으로 하는 스택 머신 어셈블리 언어입니다. + +스택 머신 프로그래밍은 후위 표기법을 사용하기 때문에 약간 이상하게 보일 수 있습니다. 즉, 연산자는 항상 연산의 끝에 있습니다. + +예를 들어, 3 + 4 대신 3 4 +를 씁니다. + +일반적인 표기법으로 (5 + 10) * 3으로 작성된 표현식은 역폴란드 표기법으로 10 5 + 3 *로 작성됩니다. + +```forth +( 이것은 주석입니다 ) + +( Unxtal의 모든 프로그래밍은 스택을 조작하여 수행됩니다 ) + +#12 ( 바이트 푸시 ) +#3456 ( 쇼트 푸시 ) + +( Uxn에는 32개의 옵코드가 있으며, 각 옵코드에는 3가지 가능한 모드가 있습니다 ) + +POP ( 바이트 팝 ) +POP2 ( 쇼트 팝 ) + +( 모드는 다음과 같습니다: + [2] 쇼트 모드는 스택에서 2바이트를 소비합니다. + [k] 유지 모드는 스택에서 항목을 소비하지 않습니다. + [r] 반환 모드는 연산자가 반환 스택에서 작동하도록 합니다. ) + +#12 #34 ADD ( 46 ) +#12 #34 ADDk ( 12 34 46 ) + +( 모드를 결합할 수 있습니다 ) + +#1234 #5678 ADD2k ( 12 34 56 78 68 ac ) + +( 산술/비트 연산 옵코드는 다음과 같습니다: + ADD SUB MUL DIV + AND ORA EOR SFT ) + +( 매크로를 사용하여 새 옵코드를 만들 수 있습니다 ) + +%MOD2 { DIV2k MUL2 SUB2 } + +#1234 #0421 MOD2 ( 01 b0 ) + +( ---------------------------------------------------------------------------- ) + +( 쇼트는 단순히 2바이트이며, 각 바이트를 조작할 수 있습니다 ) + +#1234 SWP ( 34 12 ) +#1234 #5678 SWP2 ( 56 78 12 34 ) +#1234 #5678 SWP ( 12 34 78 56 ) + +( 쇼트의 개별 바이트를 스택에서 제거할 수 있습니다 ) + +#1234 POP ( 12 ) +#1234 NIP ( 34 ) + +( 스택 옵코드는 다음과 같습니다: + POP DUP NIP SWP OVR ROT ) + +( ---------------------------------------------------------------------------- ) + +( 스택의 값을 서로 비교하려면 ) + +#12 #34 EQU ( 00 ) +#12 #12 EQU ( 01 ) + +( 논리 옵코드는 00 또는 01의 값을 가진 플래그를 넣습니다 ) + +#12 #34 LTH +#78 #56 GTH + #0101 EQU2 ( 01 ) + +( 논리 옵코드는 다음과 같습니다: + EQU NEQ GTH LTH ) + +( ---------------------------------------------------------------------------- ) + +( Uxn의 액세스 가능한 메모리는 다음과 같습니다: + 256바이트의 작업 스택 + 256바이트의 반환 스택 + 65536바이트의 메모리 + 256바이트의 IO 메모리 ) + +( 주소 지정 가능한 메모리는 0000-ffff 사이입니다 ) + +#12 #0200 STA ( 메모리의 0200에 12 저장 ) +#3456 #0201 STA2 ( 메모리의 0201에 3456 저장 ) +#0200 LDA2 ( 12 34 ) + +( 제로 페이지는 단일 바이트로 주소 지정할 수 있습니다 ) + +#1234 #80 STZ2 ( 0080에 12, 0081에 34 저장 ) +#80 LDZ2 ( 12 34 ) + +( 장치는 Uxn이 외부 세계와 통신하는 방법입니다 + Uxn에 한 번에 최대 16개의 장치가 연결됩니다 + 장치 바이트는 포트라고 하며, 콘솔 장치는 10-1f 포트를 사용합니다 + 콘솔의 포트 18은 /write라고 합니다 ) + +%EMIT { #18 DEO } + +#31 EMIT ( 콘솔에 "1" 인쇄 ) + +( 레이블은 프로그램의 위치와 같습니다 ) +@parent ( "parent"라는 레이블 정의 ) + &child ( "parent/child"라는 하위 레이블 정의 ) + +( 레이블 위치를 스택에 푸시할 수 있습니다 ) +;parent ( 절대 위치 푸시, 2바이트 ) +,parent ( 상대 위치 푸시, 1바이트 ) +.parent ( 제로 페이지 위치 푸시, 1바이트 ) + +( 메모리 옵코드는 다음과 같습니다: + LDZ STZ LDR STR + LDA STA DEI DEO ) + +( ---------------------------------------------------------------------------- ) + +( 논리를 사용하여 조건문을 만들 수 있습니다 ) + +#12 #34 NEQ ,skip JCN + #31 EMIT + @skip + +( 논리를 사용하여 for 루프를 만들 수도 있습니다 ) + +#3a #30 +@loop + DUP EMIT ( 콘솔에 "0123456789" 인쇄 ) + INC GTHk ,loop JCN +POP2 + +( 논리를 사용하여 while 루프를 만들 수도 있습니다 ) + +;word +@while + LDAk EMIT + INC2 LDAk ,while JCN +POP2 +BRK + +@word "vermillion $1 + +( 서브루틴은 JSR로 점프하고 JMP2r로 반환할 수 있습니다 ) + +;word ,print-word JSR +BRK + +@print-word ( word* -- ) + @while + LDAk EMIT + INC2 LDAk ,while JCN + POP2 +JMP2r + +@word "cerulean + +( 점프 옵코드는 다음과 같습니다: + JMP JCN JSR ) +``` + +## 더 배울 준비가 되셨나요? + +* [Uxntal 수업](https://compudanzas.net/uxn_tutorial.html) +* [Uxntal 어셈블리](https://wiki.xxiivv.com/site/uxntal.html) +* [Uxntal 자료](https://github.com/hundredrabbits/awesome-uxn) diff --git a/ko/v.md b/ko/v.md new file mode 100644 index 0000000000..14732c562a --- /dev/null +++ b/ko/v.md @@ -0,0 +1,226 @@ +--- +name: V +filename: vlang.v +contributors: + - ["Maou Shimazu", "https://github.com/Maou-Shimazu"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +V는 유지보수 가능한 소프트웨어를 구축하기 위해 설계된 정적 타입 컴파일 프로그래밍 언어입니다. + +Go와 유사하며 Oberon, Rust, Swift, Kotlin, Python의 영향도 받았습니다. + +이 언어는 최소한의 추상화로 간단하고 명확한 코드를 작성하도록 권장합니다. + +단순함에도 불구하고 V는 개발자에게 많은 강력한 기능을 제공합니다. +다른 언어에서 할 수 있는 모든 것을 V에서도 할 수 있습니다. + +```v +// 한 줄 주석. +/* + 여러 줄 주석 +*/ + +struct User { // main 함수 내부에 정의할 수 없으며, 나중에 설명합니다. + age int + name string + pos int = -1 // 사용자 정의 기본값 +} +// 구조체 메서드 +fn (u User) can_register() bool { + return u.age > 16 +} + +struct Parser { + token Token +} + +// c와 유사한 열거형 +enum Token { + plus + minus + div + mult +} + +// 1. 함수 +// 언어는 세미콜론을 사용하지 않습니다 +fn add(x int, y int) int { + return x + y +} +// 여러 값을 반환할 수 있습니다 +fn foo() (int, int) { + return 2, 3 +} + +// 함수 가시성 +pub fn public_function() { // pub는 명명된 모듈에서만 사용할 수 있습니다. +} + +fn private_function() { +} + + + +// 메인 함수 +fn main() { + // 익명 함수는 다른 함수 내부에 선언할 수 있습니다: + double_fn := fn (n int) int { + return n + n + } + // 2. 변수: 기본적으로 불변입니다 + // 암시적 타입 + x := 1 + // x = 2 // 오류 + mut y := 2 + y = 4 + name := "John" + large_number := i64(9999999999999) + println("$x, $y, $name, $large_number") // 1, 4, John, 9999999999999 + + // 함수에서 값 언패킹. + a, b := foo() + println("$a, $b") // 2, 3 + c, _ := foo() // `_`를 사용하여 값 무시 + println("$c") // 2 + + // 숫자 + u := u16(12) + v := 13 + u // v는 `u16` 타입입니다 + r := f32(45.6) + q := r + 3.14 // x는 `f32` 타입입니다 + s := 75 // a는 `int` 타입입니다 + l := 14.7 // b는 `f64` 타입입니다 + e := u + s // c는 `int` 타입입니다 + d := l + r // d는 `f64` 타입입니다 + + // 문자열 + mut bob := 'Bob' + assert bob[0] == u8(66) // 인덱싱은 바이트를 반환, u8(66) == `B` + assert bob[1..3] == 'ob' // 슬라이싱은 문자열 'ob'를 반환 + bobby := bob + 'by' // +는 문자열을 연결하는 데 사용됩니다 + println(bobby) // "Bobby" + bob += "by2" // +=는 문자열에 추가하는 데 사용됩니다 + println(bob) // "Bobby2" + + //문자열 값은 불변입니다. 요소를 변경할 수 없습니다: + //mut s := 'hello 🌎' + //s[0] = `H` // 허용되지 않음 + + //원시 문자열의 경우 r을 앞에 붙입니다. 원시 문자열에 대해서는 이스케이프 처리가 수행되지 않습니다: + rstring := r'hello\nworld' // `\n`은 두 문자로 유지됩니다 + println(rstring) // "hello\nworld" + + // 문자열 보간 + println('Hello, $bob!') // Hello, Bob! + println('Bob length + 10: ${bob.len + 10}!') // Bob length + 10: 13! + + // 3. 배열 + mut numbers := [1, 2, 3] + println(numbers) // `[1, 2, 3]` + numbers << 4 // <<로 요소 추가 + println(numbers[3]) // `4` + numbers[1] = 5 + println(numbers) // `[1, 5, 3]` + // numbers << "John" // 오류: `numbers`는 숫자 배열입니다 + numbers = [] // 배열이 이제 비어 있습니다 + arr := []int{len: 5, init: -1} + // `arr == [-1, -1, -1, -1, -1]`, arr.cap == 5 + + number_slices := [0, 10, 20, 30, 40] + println(number_slices[1..4]) // [10, 20, 30] + println(number_slices[..4]) // [0, 10, 20, 30] + println(number_slices[1..]) // [10, 20, 30, 40] + + // 4. 구조체 및 열거형 + // struct User { + // age int + // name string + // pos int = -1 // 사용자 정의 기본값 + // } + mut users := User{21, 'Bob', 0} + println(users.age) // 21 + + // enum Token { + // plus + // minus + // div + // mult + // } + + // struct Parser { + // token Token + // } + parser := Parser{} + if parser.token == .plus || parser.token == .minus + || parser.token == .div || parser.token == .mult { + // ... + } + + + // 5. 맵 + number_map := { + 'one': 1 + 'two': 2 + } + println(number_map) // {'one': 1, 'two': 2} + println(number_map["one"]) // 1 + mut m := map[string]int{} // `string` 키와 `int` 값을 갖는 맵 + m['one'] = 1 + m['two'] = 2 + println(m['one']) // "1" + println(m['bad_key']) // "0" + m.delete('two') + + // 6. 조건문 + a_number := 10 + b_number := 20 + if a_number < b { + println('$a_number < $b_number') + } else if a_number > b { + println('$a_number > $b_number') + } else { + println('$a_number == $b_number') + } + num := 777 + even_odd := if num % 2 == 0 { 'even' } else { 'odd' } + println(even_odd) + + match even_odd { + 'even' { println('even') } + 'odd' { println('odd') } + else { println('unknown') } + } + + // 7. 루프 + loops := [1, 2, 3, 4, 5] + for lp in loops { + println(lp) + } + loop_names := ['Sam', 'Peter'] + for i, lname in loop_names { + println('$i) $lname') + // 출력: 0) Sam + // 1) Peter + } + // break 및 continue 다음에 레이블 이름을 사용하여 + // 외부 for 루프를 참조할 수도 있습니다: + outer: for i := 4; true; i++ { + println(i) + for { + if i < 7 { + continue outer + } else { + break outer + } + } + } +} +``` + +## 더 읽을거리 + +V에는 공식 [V 문서](https://github.com/vlang/v/blob/master/doc/docs.md)에서 배울 수 있는 더 복잡한 개념이 있습니다. + +V 언어에 대한 자세한 정보는 [공식 웹사이트](https://vlang.io/)에서 찾거나 [v 플레이그라운드](https://v-wasm.vercel.app/)에서 확인할 수 있습니다. diff --git a/ko/vala.md b/ko/vala.md new file mode 100644 index 0000000000..1716263809 --- /dev/null +++ b/ko/vala.md @@ -0,0 +1,503 @@ +--- +name: Vala +contributors: + - ["Milo Gilad", "https://github.com/Myl0g"] +filename: LearnVala.vala +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +GNOME의 말에 따르면, "Vala는 C로 작성된 애플리케이션 및 라이브러리와 비교하여 추가 런타임 요구 사항을 부과하지 않고 다른 ABI를 사용하지 않고도 GNOME 개발자에게 최신 프로그래밍 언어 기능을 제공하는 것을 목표로 하는 프로그래밍 언어입니다." + +Vala는 Java와 C#의 측면을 가지고 있으므로 둘 중 하나를 아는 사람들에게는 자연스러울 것입니다. + +[여기서 더 읽어보세요.](https://wiki.gnome.org/Projects/Vala) + +```vala +// 한 줄 주석 + +/* 여러 줄 +주석 */ + +/** +* 문서 주석 +*/ + +/* 데이터 유형 */ + +char character = 'a' +unichar unicode_character = 'u' // 32비트 유니코드 문자 + +int i = 2; // 정수는 보장된 크기를 가질 수도 있습니다(예: int64, uint64). +uint j = -6; // 컴파일되지 않음; 부호 없는 정수는 양수만 가능합니다. + +long k; + +short l; +ushort m; + +string text = "Hello,"; // == 연산자는 문자열 내용을 확인합니다. + +string verbatim = """이것은 있는 그대로의(a.k.a. 원시) 문자열입니다. 특수 문자 +(예: \n 및 "")는 해석되지 않습니다. 여러 줄일 수도 있습니다."""; + +// 문자열 템플릿을 사용하면 쉽게 문자열 서식을 지정할 수 있습니다. +string string_template = @"$text world"; // "$text"는 "Hello,"로 평가됩니다. + +int test = 5; +int test2 = 10; +string template2 = @"$(test * test2) is a number."; // 표현식 평가 + +string template_slice = string_template[7:12]; // => "world" + +// 대부분의 데이터 유형에는 구문 분석을 위한 메서드가 있습니다. + +bool parse_bool = bool.parse("false"); // => false +int parse_int = int.parse("-52"); // => -52 +string parse_string = parse_int.to_string(); // => "-52" + +/* 기본 I/O */ + +stdout.printf(parse_string); // 콘솔에 인쇄 +string input = stdin.read_line(); // 콘솔에서 입력 받기 + +stderr.printf("Error message"); // 오류 인쇄 + +/* 배열 */ + +int[] int_array = new int[10]; // 10개의 슬롯이 있는 정수 배열 +int better_int_array[10]; // 위의 표현식, 단축 +int_array.length; // => 10; + +int[] int_array2 = {5, 10, 15, 20}; // 즉시 생성 가능 + +int[] array_slice = int_array2[1:3]; // 슬라이스(데이터 복사) +unowned int[] array_slice_ref = int_array2[1:3]; // 데이터에 대한 참조 + +// 다차원 배열(대괄호 안의 쉼표 수로 정의) + +int[,] multi_array = new int[6,4]; // 6은 배열 수, 4는 크기 +int[,] multi_array2 = {{7, 4, 6, 4}, + {3, 2, 4, 6}, + {5, 9, 5, 1}}; // new int[3,4] +multi_array2[2,3] = 12; // 2는 배열, 3은 배열의 인덱스 +int first_d = multi_array2.length[0] // => 3 +int second_d = multi_array2.length[1] // => 4 + +// 배열 길이가 다른 스택 배열(예: int[][])은 지원되지 않습니다. + +// 다차원 배열은 슬라이스할 수 없으며 1차원으로 변환할 수도 없습니다. + +int[] add_to_array = {}; +add_to_array += 12; // 배열에 동적으로 추가할 수 있습니다. + +add_to_array.resize(20); // 배열에 이제 20개의 슬롯이 있습니다. + +uint8[] chars = "test message".data; +chars.move(5, 0, 7); +stdout.printf((string) chars); // 배열을 문자열로 캐스팅하고 인쇄합니다. + +/* 제어 흐름 */ + +int a = 1; +int b = 2; +int[] foreach_demo = {2, 4, 6, 8}; + +while (b > a) { // While 루프; 실행 전에 표현식이 참인지 확인 + b--; +} + +do { + b--; +} +while (b > a); // Do While 루프; while (b > a) 전에 "do"의 코드를 실행 + +for (a = 0; a < 10; a++) { stdout.printf("%d\n", a); } // for 루프 + +foreach (int foreach_demo_var in foreach_demo) { + stdout.printf("%d\n", foreach_demo_var); +} // foreach는 모든 반복 가능한 컬렉션에서 작동합니다. + +if (a == 0) { + stdout.printf("%d\n", a); +} else if (a > 1) { + stdout.printf("%d\n", a); +} else { + stdout.printf("A is less than 0"); +} // if-then-else + +switch (a) { + case 1: + stdout.printf("A is 1\n"); + break; + case 5: + case 10: + stdout.printf("A is 5 or 10\n"); + break; + default: + stdout.printf("???\n") + break; +} // switch 문 + +/* 유형 캐스팅 및 추론 */ + +int cast_to_float = 10; +float casted_float = (float) cast_to_float; // 정적 캐스팅; 런타임 검사 없음 + +// 런타임 검사의 경우 동적 캐스팅을 사용합니다. +// 동적으로 캐스팅된 객체는 다음 중 하나여야 합니다. +// - 객체의 클래스가 원하는 유형과 동일한 클래스입니다. +// - 객체의 클래스가 원하는 유형의 하위 클래스입니다. +// - 원하는 클래스가 객체의 클래스에서 구현된 인터페이스입니다. + +float dyna_casted_float = cast_to_float as float // 컴파일되지 않음 + +var inferred_string = "hello"; // 유형 추론 + +/* 메서드 (a.k.a. 함수) */ + +int method_demo(string arg1, Object arg2) { // int를 반환하고 인수를 받습니다. + return 1; +} + +// Vala 메서드는 오버로드할 수 없습니다. + +void some_method(string text) { } +void some_method(int number) { } // 컴파일되지 않음 + +// 유사한 기능을 달성하려면 기본 인수 값을 사용하십시오. + +void some_better_method(string text, int number = 0) { } + +some_better_method("text"); +some_better_method("text", 12); + +// varargs(가변 길이 인수 목록)도 지원됩니다. + +void method_with_varargs(int arg1, ...) { + var varargs_list = va_list(); // varargs 목록을 가져옵니다. + + string arg_string = varargs_list.arg(); // 인수를 차례로 가져옵니다. + int int_vararg = varargs_list.arg(); + + stdout.printf("%s, %d\n", arg_string, int_vararg) +} + +string? ok_to_be_null(int? test_int) { } // "?"는 가능한 null 값을 나타냅니다. + +// 대리자 + +delegate void DelegateDemo(char char_a); + +void delegate_match(char char_a) { // DelegateDemo의 서명과 일치합니다. + stdout.printf("%d\n"); +} + +void call_delegate(DelegateDemo d, char char_b) { // 대리자 인수를 받습니다. + d(char_b) // 대리자 호출 +} + +void final_delegate_demo() { + call_delegate(delegate_match); // 일치하는 메서드를 인수로 전달합니다. +} + +// 람다(a.k.a. 익명 메서드)는 "=>"로 정의됩니다. + +(a) => { stdout.printf("%d\n", a); } // "a"를 인쇄합니다. + +/* 네임스페이스 */ + +namespace NamespaceDemo { + // 변수 이름을 구성할 수 있습니다. + int namespace_int = 12; +} +namespace_int += 5; // 컴파일되지 않음 + +using NamespaceDemo; +namespace_int += 5; // 유효함 + +/* 구조체 및 열거형 */ + +struct Closet { + public uint shirts; // 기본 액세스 한정자는 private입니다. + public uint jackets; +} + +Closet struct_init_1 = Closet(); // 또는 Closet struct_init_1 = {}; +Closet struct_init_2 = {15, 3}; +var struct_init_3 = Closet() { // 유형 추론도 작동합니다. + shirts = 15; + jackets = 3; +} + +enum HouseSize { // 열거형의 예 + SMALL, + MODERATE, + BIG +} + +/* 클래스 및 객체 지향 프로그래밍 */ + +class Message : GLib.Object { // 클래스 Message는 GLib의 Object를 확장합니다. + private string sender; // private 필드 + public string text {get; set;} // public 속성 (나중에 자세히 설명) + protected bool is_digital = true; // protected (이 클래스 및 하위 클래스) + internal bool sent = false; // internal (동일한 패키지의 클래스) + + public void send(string sender) { // public 메서드 + this.sender = sender; + sent = true; + } + + public Message() { // 생성자 + // ... + } + +} + +// 메서드 오버로딩이 불가능하므로 생성자를 오버로드할 수 없습니다. +// 그러나 명명된 생성자를 사용하여 동일한 기능을 달성할 수 있습니다. + +public class Calculator : GLib.Object { + + public Calculator() { + } + + public Calculator.with_name(string name) { + } + + public Calculator.model(string model_id, string name = "") { + this.with_name(@"$model_id $name"); // "this"를 사용한 연쇄 생성자 + } + ~Calculator() { } // 수동 메모리 관리를 사용하는 경우에만 필요합니다. +} + +var calc1 = new Calculator.with_name("Temp"); +var calc2 = new Calculator.model("TI-84"); + +// 시그널(a.k.a. 이벤트 또는 이벤트 리스너)은 동일한 서명을 가진 여러 +// 메서드를 동시에 실행하는 방법입니다. + +public class SignalDemo : GLib.Object { + public signal void sig_demo(int sig_demo_int); // public이어야 합니다. + + public static int main(string[] args) { + // main 메서드; 프로그램은 이것 없이는 컴파일되지 않습니다. + + var sig_demo_class = new SignalDemo(); // 클래스의 새 인스턴스 + + sig_demo_class.sig_demo.connect((ob, sig_int) => { // 람다를 핸들러로 사용 + stdout.printf("%d\n", sig_int); // "ob"는 방출된 객체입니다. + }); + + sig_demo_class.sig_demo(27); // 시그널이 방출됩니다. + + return 0; + } +} + +// connect() 메서드를 사용하고 원하는 만큼 핸들러를 연결할 수 있습니다. +// 시그널이 방출되면 모두 거의 동시에 실행됩니다. + +// 속성 (getter 및 setter) + +class Animal : GLib.Object { + private int _legs; // 이름 충돌을 방지하기 위해 밑줄로 시작 + + public int legs { + get { return _legs; } + set { _legs = value; } + } + + public int eyes { get; set; default = 5; } // 더 짧은 방법 + public int kingdom { get; private set; default = "Animalia"} // 읽기 전용 + + public static void main(string args[]) { + rabbit = new Animal(); + + // 모든 GLib.Object에는 속성이 변경될 때 방출되는 "notify" 시그널이 있습니다. + + // 특정 속성을 지정하는 경우 GObject 명명 규칙을 따르기 위해 + // 모든 밑줄을 대시로 바꿉니다. + + rabbit.notify["eyes"].connect((s, p) => { // 모든 것에 대해 ["eyes"] 제거 + stdout.printf("Property '%s' has changed!\n", p.name); + }); + + rabbit.legs = 2; + rabbit.legs += 2; + rabbit.eyes = 2; + + } +} + +// 상속: Vala 클래스는 1개의 클래스를 상속할 수 있습니다. 상속은 암시적이지 않습니다. + +class SuperDemo : GLib.Object { + public int data1; + protected int data2; + internal int data3; + private int data4; + + public static void test_method { } // 정적은 객체 없이 호출할 수 있습니다. +} +class SubDemo : SuperDemo { + public static void main(string args[]) { + stdout.printf((string) data1); // 컴파일됨 + stdout.printf((string) data2); // Protected는 하위 클래스에서 액세스 가능 + stdout.printf((string) data3); // Internal은 패키지에서 액세스 가능 + stdout.printf((string) data4); // 컴파일되지 않음 + } +} + +// 추상 클래스 및 메서드 + +public abstract class OperatingSystem : GLib.Object { + public void turn_on() { + stdout.printf("Booted successfully.\n"); + } + public abstract void use_computer(); +} + +public class Linux : OperatingSystem { + public override void use_computer() { // 추상 메서드는 재정의해야 합니다. + stdout.printf("Beep boop\n"); + } +} + +// 추상 메서드에 기본 동작을 추가하려면 "virtual"로 만드십시오. + +public abstract class HardDrive : GLib.Object { + public virtual void die() { + stdout.printf("CLICK-CLICK-CLICK\n"); + } +} +public class MyHD : HardDrive { + public override void die() { + return; + } +} + +// 인터페이스: 클래스는 이들 중 원하는 수만큼 구현할 수 있습니다. + +interface Laptop { // 추상 또는 가상만 포함할 수 있습니다. + public abstract void turn_on(); + public abstract void turn_off(); + + public abstract int cores; // 컴파일되지 않음; 필드는 추상일 수 없음 + public abstract int cores {get; set;} // 컴파일됨 + + public virtual void keyboard() { // 가상은 허용됨 (Java/C#과 달리) + stdout.printf("Clickity-clack\n"); + } +} + +// Vala에서 가상을 사용할 수 있다는 것은 다중 상속이 +// 가능하다는 것을 의미합니다 (다소 제한적이지만). + +// 인터페이스는 인터페이스를 구현할 수 없지만 특정 +// 인터페이스나 클래스도 구현해야 한다는 것을 지정할 수 있습니다 (사전 요구 사항). + +public interface CellPhone : Collection, GLib.Object {} + +// 런타임에 동적으로 클래스의 유형 정보를 얻을 수 있습니다. + +bool type_info = object is TypeName; // "is"를 사용하여 bool을 얻습니다. + +Type type_info2 = object.get_type(); +var type_name = type_info2.name(); + +Type type_info3 = typeof(Linux); +Linux type_demo = (Linux) Object.new(type_info3); + +// 제네릭 + +class Computer : GLib.Object { + private OperatingSystem os; + + public void install_os(OperatingSystem os) { + this.os = os; + } + public OperatingSystem retrieve_os() { + return this.os; + } +} + +var new_computer = new Computer(); + +/* 기타 기능 */ + +// 어설션: 문이 참이 아닌 경우 충돌 (런타임에) + +bool is_true = true; +assert(is_true); + +// 계약 프로그래밍 + +int contract_demo(int arg1, int arg2) { + requires(arg1 > 0 && arg1 < 10) // 세미콜론 없음 참고 + requires(arg2 >= 12) + ensures(result >= 0) +} + +// 오류 처리 + +void error_demo(int int_ex) throws GError { + if (int_ex != 1) { + throw new GError("TEST MESSAGE"); + } +} +void error_demo2() { + try { + error_demo(0); + } catch (GError ge) { + stdout.printf("%s\n", ge.message); + } +} + +// 메인 루프 + +void main() { + + var main_loop = new MainLoop(); + var time = new TimeoutSource(2000); + + time.set_callback(() => { // 2000ms 후에 다음 람다를 실행합니다. + stdout.printf("2000ms have passed\n"); + main_loop.quit(); + return false; + }); + + time.attach(main_loop.get_context()); + + loop.run(); +} + +// 포인터 (수동 메모리 관리) + +Object* pointer_obj = new Object(); // Object 인스턴스를 만들고 포인터를 제공합니다. + +pointer_obj->some_method(); // some_method 실행 +pointer_obj->some_data; // some_data 반환 + +delete pointer_obj; + +int more = 57; +int* more_pointer = &more; // & = 주소-of +int indirection_demo = more_pointer*; // 간접 참조 + +// 프로필: 어떤 Vala 기능이 사용 가능하고 C 코드가 어떤 라이브러리를 +// 사용할지에 영향을 줍니다. +// - gobject (기본값) +// posix +// dova +// 컴파일할 때 "--profile=whatever"를 사용하십시오. +``` + +* 더 많은 [Vala 문서](https://valadoc.org/). +* GObject와 유사한 [대체 생성 구문](https://wiki.gnome.org/Projects/Vala/Tutorial#GObject-Style_Construction) +* [계약 프로그래밍](http://en.wikipedia.org/wiki/Contract_programming)에 대한 자세한 정보 +* [컬렉션 라이브러리](https://wiki.gnome.org/Projects/Vala/Tutorial#Collections) +* [멀티스레딩](https://wiki.gnome.org/Projects/Vala/Tutorial#Multi-Threading) +* [GTK+ 및 Vala로 GUI 빌드](http://archive.is/7C7bw)에 대해 읽어보십시오. +* [D-Bus 통합](https://wiki.gnome.org/Projects/Vala/Tutorial#D-Bus_Integration) diff --git a/ko/vimscript.md b/ko/vimscript.md new file mode 100644 index 0000000000..9e8e7440de --- /dev/null +++ b/ko/vimscript.md @@ -0,0 +1,609 @@ +--- +name: Vimscript +filename: learnvimscript.vim +contributors: + - ["HiPhish", "http://hiphish.github.io/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```vim +" ############## +" 소개 +" ############## +" +" Vim 스크립트(VimL이라고도 함)는 값, 변수, 함수 또는 루프와 같이 스크립팅 언어에서 기대할 수 있는 여러 기능을 제공하는 Vim의 ex-명령어 하위 집합입니다. +" Vim 스크립트 파일은 단지 ex-명령어의 시퀀스라는 것을 항상 염두에 두십시오. 스크립트가 프로그래밍 언어 기능과 원시 ex-명령어를 혼합하는 것은 매우 일반적입니다. +" +" 명령줄 모드에서 명령을 직접 입력하여 Vim 스크립트를 실행하거나(명령줄 모드로 들어가려면 `:` 누름) 파일에 작성(선행 `:` 없이)하고 실행 중인 Vim 인스턴스에서 소싱할 수 있습니다(`:source path/to/file`). +" 일부 파일은 구성의 일부로 자동으로 소싱됩니다(|startup| 참조). 이 가이드는 ex-명령어에 익숙하고 스크립팅만 다룰 것이라고 가정합니다. 관련 매뉴얼 섹션에 대한 도움말 항목이 포함되어 있습니다. +" +" Vim 스크립트에 대한 공식 소개는 |usr_41.txt|를 참조하십시오. 주석은 일치하지 않는 `"` 다음에 오는 모든 것이 줄 끝까지이며, `|`는 지침을 구분합니다(대부분의 다른 언어에서 `;`가 하는 역할). |help.txt|와 같이 `|`로 둘러싸인 매뉴얼에 대한 참조. + +" 이것은 주석입니다 + +" 세로줄 '|' (파이프)는 명령을 구분합니다 +echo 'Hello' | echo 'world!' + +" 명령어 뒤에 주석을 다는 것은 보통 작동합니다 +pwd " 현재 작업 디렉토리를 표시합니다 + +" 일부 명령어를 제외하고는 그렇지 않습니다. 주석 앞에 명령어 구분 기호를 사용하십시오(echo는 따옴표가 문자열을 시작한다고 가정합니다) +echo 'Hello world!' | " 메시지를 표시합니다 + +" 줄 바꿈은 다음 줄의 첫 번째 비공백 문자로 백슬래시를 배치하여 이스케이프할 수 있습니다. 스크립트 파일에서만 작동하며 명령줄에서는 작동하지 않습니다 +echo " Hello + \ world " + +echo [1, + \ 2] + +echo { + \ 'a': 1, + \ 'b': 2 +\} + + +" ####### +" 타입 +" ####### +" +" 타입에 대한 개요는 |E712|를 참조하십시오. 연산자에 대한 개요는 |expression-syntax|를 참조하십시오. + +" 숫자 (|expr-number|) +" ####### + +echo 123 | " 십진수 +echo 0b1111011 | " 이진수 +echo 0173 | " 8진수 +echo 0x7B | " 16진수 +echo 123.0 | " 부동 소수점 +echo 1.23e2 | " 부동 소수점 (과학적 표기법) + +" 선행 `0`이 있는 *정수*는 8진수 표기법입니다. +" 일반적인 산술 연산이 지원됩니다. + +echo 1 + 2 | " 덧셈 +echo 1 - 2 | " 뺄셈 +echo - 1 | " 부정 (단항 빼기) +echo + 1 | " 단항 더하기 (실제로는 아무것도 하지 않지만 여전히 합법적) +echo 1 * 2 | " 곱셈 +echo 1 / 2 | " 나눗셈 +echo 1 % 2 | " 나머지 (나머지) + +" 불리언 (|Boolean|) +" ######## +" +" 숫자 0은 거짓이고 다른 모든 숫자는 참입니다. 문자열은 암시적으로 숫자로 변환됩니다(아래 참조). +" 두 개의 미리 정의된 의미 상수. + +echo v:true | " 1 또는 문자열 'v:true'로 평가됩니다 +echo v:false | " 0 또는 문자열 'v:false'로 평가됩니다 + +" 불리언 값은 두 객체의 비교 결과일 수 있습니다. + +echo x == y | " 값에 의한 동등성 +echo x != y | " 불일치 +echo x > y | " 보다 큼 +echo x >= y | " 크거나 같음 +echo x < y | " 보다 작음 +echo x <= y | " 작거나 같음 +echo x is y | " 인스턴스 ID (리스트 및 딕셔너리) +echo x isnot y | " 인스턴스 비 ID (리스트 및 딕셔너리) + +" 문자열은 영숫자 순서에 따라 비교됩니다 +" echo 'a' < 'b'. 대소문자 구분은 'ignorecase' 설정에 따라 다릅니다. +" +" 명시적인 대소문자 구분은 연산자에 '#' (대소문자 일치) 또는 '?' (대소문자 무시)를 추가하여 지정됩니다. +" 이식 가능한 스크립트를 작성할 때 명시적으로 대소문자 구분을 선호하십시오. + +echo 'a' < 'B' | " 'ignorecase'에 따라 참 또는 거짓 +echo 'a' x * x} | " 익명 함수 +echo function('substitute', ['hello']) | " 부분 함수 + + +" 정규식 (|regular-expression|) +" ################## +" +" 정규식 패턴은 일반적으로 문자열이지만, 경우에 따라 구분 기호 쌍(보통 `/`이지만 원하는 것을 선택할 수 있음) 사이에 정규식을 사용할 수도 있습니다. + +" 'hello'를 'Hello'로 대체 +substitute/hello/Hello/ + + +" ########################### +" 암시적 타입 변환 +" ########################### +" +" 문자열은 숫자로, 숫자는 필요할 때 문자열로 변환됩니다. +" 숫자는 10진수 표기법으로 문자열이 됩니다. 문자열은 숫자로 구문 분석할 수 있으면 숫자 값이 되고, 그렇지 않으면 0이 됩니다. + +echo "1" + 1 | " 숫자 +echo "1" .. 1 | " 문자열 +echo "0xA" + 1 | " 숫자 + +" 문자열은 불리언으로 사용될 때 숫자처럼 취급됩니다 +echo "true" ? 1 : 0 | " 이 문자열은 0으로 구문 분석되어 거짓입니다 + +" ########### +" 변수 +" ########### +" +" 변수는 범위 내에 바인딩됩니다. 범위가 제공되지 않으면 Vim에서 기본값을 선택합니다. +" 값을 바인딩하려면 `:let` 및 `:const`를 사용하고, 바인딩을 해제하려면 `:unlet`을 사용하십시오. + +let b:my_var = 1 | " 현재 버퍼에 로컬 +let w:my_var = 1 | " 현재 창에 로컬 +let t:my_var = 1 | " 현재 탭 페이지에 로컬 +let g:my_var = 1 | " 전역 변수 +let l:my_var = 1 | " 현재 함수에 로컬 (아래 함수 참조) +let s:my_var = 1 | " 현재 스크립트 파일에 로컬 +let a:my_arg = 1 | " 함수 인수 (아래 함수 참조) + +" Vim 범위는 읽기 전용입니다 +echo v:true | " 특수 내장 Vim 변수 (|v:var|) + +" 변수와 같은 특수 Vim 메모리 액세스 +let @a = 'Hello' | " 레지스터 +let $PATH='' | " 환경 변수 +let &textwidth = 79 | " 옵션 +let &l:textwidth = 79 | " 로컬 옵션 +let &g:textwidth = 79 | " 전역 옵션 + +" 딕셔너리로 범위 액세스 (모든 딕셔너리처럼 수정 가능) +" 액세스 및 조작에 대해서는 |dict-functions|, 특히 |get()|을 참조하십시오. +echo b: | " 모든 버퍼 변수 +echo w: | " 모든 창 변수 +echo t: | " 모든 탭 페이지 변수 +echo g: | " 모든 전역 변수 +echo l: | " 모든 로컬 변수 +echo s: | " 모든 스크립트 변수 +echo a: | " 모든 함수 인수 +echo v: | " 모든 Vim 변수 + +" 상수 변수 +const x = 10 | " |:const|, |:lockvar| 참조 + +" 함수 참조 변수는 함수 이름과 동일한 제한을 가집니다 +let IsString = {x -> type(x) == type('')} | " 전역: 대문자 +let s:isNumber = {x -> type(x) == type(0)} | " 로컬: 모든 이름 허용 + +" 생략하면 `g:` 범위가 암시되지만, 함수에서는 `l:`이 암시됩니다. + + +" 다중 값 바인딩 (리스트 언패킹) +" ####################################### +" +" 리스트 값을 여러 변수에 할당 (항목 수가 일치해야 함) +let [x, y] = [1, 2] + +" 나머지를 나머지 변수에 할당 (세미콜론 참고) +let [mother, father; children] = ['Alice', 'Bob', 'Carol', 'Dennis', 'Emily'] + + +" ############## +" 제어 흐름 +" ############## + +" 조건문 (|:if|, |:elseif|, |:else|, |:endif|) +" ########### +" +" 조건은 `if`와 `endif` 사이에 설정됩니다. 중첩될 수 있습니다. + +let condition = v:true + +if condition + echo 'First condition' +elseif another_condition + echo 'Second condition' +else + echo 'Fail' +endif + +" 루프 (|:for|, |:endfor|, |:while|, |:endwhile|, |:break|, |:continue|) +" ##### +" +" 두 가지 유형의 루프: `:for` 및 `:while`. 다음 반복으로 건너뛰려면 `:continue`를 사용하고, 루프를 빠져나가려면 `:break`를 사용하십시오. + +" For-루프 (|:for|, |:endfor|) +" ======== +" +" For-루프는 리스트만 반복합니다. 다른 시퀀스를 반복하려면 리스트를 생성하는 함수를 사용해야 합니다. + +" 리스트 반복 +for person in ['Alice', 'Bob', 'Carol', 'Dennis', 'Emily'] + echo 'Hello ' .. person +endfor + +" 중첩된 리스트를 언패킹하여 반복 +for [x, y] in [[1, 0], [0, 1], [-1, 0], [0, -1]] + echo 'Position: x =' .. x .. ', y = ' .. y +endfor + +" 숫자 범위 반복 +for i in range(10, 0, -1) " 10부터 카운트다운 + echo 'T minus' .. i +endfor + +" 딕셔너리 키 반복 +for symbol in keys({'π': 3.14, 'e': 2.71}) + echo 'The constant ' .. symbol .. ' is a transcendent number' +endfor + +" 딕셔너리 값 반복 +for value in values({'π': 3.14, 'e': 2.71}) + echo 'The value ' .. value .. ' approximates a transcendent number' +endfor + +" 딕셔너리 키와 값 반복 +for [symbol, value] in items({'π': 3.14, 'e': 2.71}) + echo 'The number ' .. symbol .. ' is approximately ' .. value +endfor + +" While-루프 (|:while|, |:endwhile|) + +let there_yet = v:true +while !there_yet + echo 'Are we there yet?' +endwhile + + +" 예외 처리 (|exception-handling|) +" ################## +" +" 새 예외를 문자열로 throw하고, 문자열에 대한 정규식 패턴 일치로 catch합니다. + +" 새 예외 throw +throw "Wrong arguments" + +" 예외로부터 보호 (두 번째 catch는 모든 예외와 일치) +try + source path/to/file +catch /Cannot open/ + echo 'Looks like that file does not exist' +catch /.*/ + echo 'Something went wrong, but I do not know what' +finally + echo 'I am done trying' +endtry + + +" ########## +" 함수 +" ########## + +" 함수 정의 (|:function|, |:endfunction|) +" ################## + +" 범위가 없는 함수 이름은 대문자로 시작해야 합니다 +function! AddNumbersLoudly(x, y) + " a: 범위를 사용하여 인수에 액세스 + echo 'Adding' .. a:x .. 'and' .. a:y | " 부작용 + return a:x + a:y | " 반환 값 +endfunction + +" 범위가 있는 함수 이름은 소문자로 시작할 수 있습니다 +function! s:addNumbersLoudly(x, y) + echo 'Adding' .. a:x .. 'and' .. a:y + return a:x + a:y +endfunction + +" 느낌표가 없으면 함수를 다시 정의하는 것이 오류가 되지만, 느낌표가 있으면 새 정의가 이전 정의를 대체할 수 있습니다. +" Vim 스크립트 파일은 세션 동안 여러 번 다시 로드될 수 있으므로, 무엇을 하는지 정말로 알지 못하는 한 느낌표를 사용하는 것이 가장 좋습니다. + +" 함수 정의에는 인수 목록 뒤에 특수 한정자가 있을 수 있습니다. + +" 범위 함수는 두 개의 암시적 인수를 정의하며, ex-명령어의 범위로 설정됩니다 +function! FirstAndLastLine() range + echo [a:firstline, a:lastline] +endfunction + +" 패턴과 일치하는 첫 번째 및 마지막 줄을 인쇄합니다 (|cmdline-ranges|) +/^#!/,/!#$/call FirstAndLastLine() + +" 함수 중단, 오류 발생 시 중단 (|:func-abort|) +function! SourceMyFile() abort + source my-file.vim | " 존재하지 않는 파일 소싱 시도 + echo 'This will never be printed' +endfunction + +" 클로저, 외부 범위에서 값을 전달하는 함수 (|:func-closure|) +function! MakeAdder(x) + function! Adder(n) closure + return a:n + a:x + endfunction + return funcref('Adder') +endfunction +let AddFive = MakeAdder(5) +echo AddFive(3) | " 8 인쇄 + +" 딕셔너리 함수, 가난한 사람의 OOP 메서드 (|Dictionary-function|) +function! Mylen() dict + return len(self.data) | " 암시적 변수 self +endfunction +let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")} +echo mydict.len() + +" 또는 더 간결하게 +let mydict = {'data': [0, 1, 2, 3]} +function! mydict.len() + return len(self.data) +endfunction + +" 함수 호출 (|:call|) +" ################# + +" 반환 값 및 부작용을 위해 함수 호출 +let animals = keys({'cow': 'moo', 'dog': 'woof', 'cat': 'meow'}) + +" 부작용만을 위해 함수 호출, 잠재적인 반환 값 무시 +call sign_undefine() + +" call() 함수는 함수 참조를 호출하고 매개변수를 리스트로 전달하며, 함수의 결과를 반환합니다. +echo call(function('get'), [{'a': 1, 'b': 2}, 'c', 3]) | " 3 인쇄 + +" Vim 스크립트는 ex-명령어 내에 포함되어 있으므로 함수를 직접 호출할 수 없고 `:call` ex-명령어를 사용해야 합니다. + +" 함수 네임스페이스 (|write-library-script|, |autoload|) +" ################### + +" autoload/foo/bar.vim에 정의해야 합니다 +" 네임스페이스 함수 이름은 대문자로 시작할 필요가 없습니다 +function! foo#bar#log(value) + echomsg value +endfunction + +call foo#bar#log('Hello') + + +" ############################# +" 자주 사용되는 ex-명령어 +" ############################# + + +" 런타임 파일 소싱 (|'runtimepath'|) +" ###################### + +" 런타임 경로 중에서 첫 번째 일치 항목 소싱 +runtime plugin/my-plugin.vim + + +" 새 ex-명령어 정의 (|40.2|, |:command|) +" ######################## + +" 첫 번째 인수는 명령어 이름, 나머지는 명령어 본문 +command! SwapAdjacentLines normal! ddp + +" 느낌표는 `:function`과 동일하게 작동합니다. 사용자 정의 명령어는 대문자로 시작해야 합니다. +" `:command` 명령어는 `-nargs`와 같이 여러 속성을 가질 수 있으며, 모두 대시로 시작하여 명령어 이름과 구분합니다. + +command! -nargs=1 Error echoerr + + +" 자동 명령어 정의 (|40.3|, |autocmd|, |autocommand-events|) +" ###################### + +" 인수는 "이벤트", "패턴", 나머지는 "명령어"입니다 +autocmd BufWritePost $MYVIMRC source $MYVIMRC + +" 이벤트와 패턴은 공백 없는 쉼표로 구분됩니다. +" 표준 이벤트는 |autocmd-events|를 참조하고, 사용자 정의 이벤트는 |User|를 참조하십시오. +" 나머지는 실행될 ex-명령어입니다. + +" 자동 그룹 +" =========== +" +" 파일이 여러 번 소싱될 때 자동 명령어가 새로 정의되어 이전 명령어를 삭제하지 않고 시간이 지남에 따라 자동 명령어가 쌓이게 됩니다. +" 이를 방지하기 위해 자동 그룹과 다음 의식을 사용하십시오. + +augroup auto-source | " 그룹 이름은 임의적입니다 + autocmd! | " 현재 그룹의 모든 자동 명령어 삭제 + autocmd BufWritePost $MYVIMRC source $MYVIMRC +augroup END | " 기본 자동 그룹으로 다시 전환 + +" 그룹을 직접 할당할 수도 있습니다. +" 그룹 정의가 한 스크립트에 있고 자동 명령어 정의가 다른 스크립트에 있는 경우 유용합니다. + +" 한 파일에서 +augroup auto-source + autocmd! +augroup END + +" 다른 파일에서 +autocmd auto-source BufWritePost $MYVIMRC source $MYVIMRC + +" 실행 (일종의 런타임 매크로) +" #################################### + +" 때로는 명령어의 일부가 런타임까지 알려지지 않은 ex-명령어를 구성해야 합니다. + +let line = 3 | " 런타임에 결정되는 줄 번호 +execute line .. 'delete' | " 한 줄 삭제 + +" 일반 모드 명령어 실행 +" ############################## +" +" `:normal`을 사용하여 명령줄에서 일반 모드 명령어 시퀀스를 재생합니다. +" 사용자 매핑을 무시하려면 느낌표를 추가하십시오. + +normal! ggddGp | " 첫 번째 줄을 버퍼 끝으로 이식 + +" 창 명령어는 :normal과 함께 사용하거나, :normal이 작동하지 않을 경우 :wincmd와 함께 사용할 수 있습니다 +wincmd L | " 현재 창을 맨 오른쪽으로 이동 + + +" ########################### +" 자주 사용되는 함수 +" ########################### + +" 기능 확인 +echo has('nvim') | " Neovim 실행 중 +echo has('python3') | " Python 3 플러그인 지원 +echo has('unix') | " 유닉스 시스템에서 실행 중 +echo has('win32') | " Windows 시스템에서 실행 중 + + +" 무언가 존재하는지 테스트 +echo exists('&mouse') | " 옵션 (존재만 함) +echo exists('+mouse') | " 옵션 (존재하고 작동함) +echo exists('$HOSTNAME') | " 환경 변수 +echo exists('*strftime') | " 내장 함수 +echo exists('**s:MyFunc') | " 사용자 정의 함수 +echo exists('bufcount') | " 변수 (범위는 선택 사항) +echo exists('my_dict["foo"]') | " 변수 (딕셔셔너리 항목) +echo exists('my_dict["foo"]') | " 변수 (딕셔셔너리 항목) +echo exists(':Make') | " 명령어 +echo exists("#CursorHold") | " 이벤트에 대해 정의된 자동 명령어 +echo exists("#BufReadPre#*.gz") | " 이벤트 및 패턴 +echo exists("#filetypeindent") | " 자동 명령어 그룹 +echo exists("##ColorScheme") | " 이벤트에 대해 지원되는 자동 명령어 + +" 다양한 동적 값 (|expand()| 참조) +echo expand('%') | " 현재 파일 이름 +echo expand('') | " 커서 아래 현재 단어 +echo expand('%:p') | " 수정자 가능 + +" 타입 테스트 +" 다음 타입에 대해 고유한 상수가 정의되어 있습니다. 이전 버전의 Vim에는 타입 변수가 없으므로 해결 방법은 참조 문서를 참조하십시오. +echo type(my_var) == v:t_number | " 숫자 +echo type(my_var) == v:t_string | " 문자열 +echo type(my_var) == v:t_func | " 함수 참조 +echo type(my_var) == v:t_list | " 리스트 +echo type(my_var) == v:t_dict | " 딕셔너리 +echo type(my_var) == v:t_float | " 부동 소수점 +echo type(my_var) == v:t_bool | " 명시적 불리언 +" null 객체의 경우 자체와 비교해야 합니다 +echo my_var is v:null + +" 문자열 서식 지정 +echo printf('%d in hexadecimal is %X', 123, 123) + + +" ##################### +" 업계의 비결 +" ##################### + +" 소스 가드 +" ############ + +" 파일이 여러 번 소싱되는 것을 방지합니다. 사용자는 구성에서 변수를 설정하여 플러그인이 전혀 로드되지 않도록 할 수 있습니다. +if exists('g:loaded_my_plugin') + finish +endif +let g:loaded_my_plugin = v:true + +" 기본값 +" ############## + +" 기본값을 가져옵니다. 사용자가 변수를 정의하면 해당 변수를 사용하고, 그렇지 않으면 하드코딩된 기본값을 사용합니다. 범위도 딕셔너리라는 사실을 이용합니다. +let s:greeting = get(g:, 'my_plugin_greeting', 'Hello') +``` diff --git a/ko/visualbasic.md b/ko/visualbasic.md new file mode 100644 index 0000000000..bf4e5eff76 --- /dev/null +++ b/ko/visualbasic.md @@ -0,0 +1,274 @@ +--- +name: Visual Basic +contributors: + - ["Brian Martin", "http://brianmartin.biz"] +filename: learnvisualbasic.vb +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +```vbnet +Module Module1 + + Sub Main() + '본격적으로 시작하기 전에 Visual Basic 콘솔 애플리케이션에 대한 간략한 개요입니다. + '아포스트로피는 주석을 시작합니다. + 'Visual Basic 컴파일러 내에서 이 튜토리얼을 탐색하기 위해 + '내비게이션 시스템을 구성했습니다. + '이 내비게이션 시스템은 이 튜토리얼을 더 깊이 파고들면서 + '설명되므로 모든 의미를 이해하게 될 것입니다. + Console.Title = ("Learn X in Y Minutes") + Console.WriteLine("NAVIGATION") '표시 + Console.WriteLine("") + Console.ForegroundColor = ConsoleColor.Green + Console.WriteLine("1. Hello World 출력") + Console.WriteLine("2. Hello World 입력") + Console.WriteLine("3. 정수 계산") + Console.WriteLine("4. 소수 계산") + Console.WriteLine("5. 작동하는 계산기") + Console.WriteLine("6. Do While 루프 사용") + Console.WriteLine("7. For While 루프 사용") + Console.WriteLine("8. 조건문") + Console.WriteLine("9. 음료 선택") + Console.WriteLine("50. 정보") + Console.WriteLine("위 목록에서 번호를 선택하십시오") + Dim selection As String = Console.ReadLine + 'Select 문의 "Case"는 선택 사항입니다. + '예를 들어, "Select Case selection" 대신 "Select selection"도 + '작동합니다. + Select Case selection + Case "1" 'HelloWorld 출력 + Console.Clear() '애플리케이션을 지우고 private sub를 엽니다. + HelloWorldOutput() 'Private Sub 이름 지정, Private Sub 열기 + Case "2" 'Hello 입력 + Console.Clear() + HelloWorldInput() + Case "3" '정수 계산 + Console.Clear() + CalculatingWholeNumbers() + Case "4" '소수 계산 + Console.Clear() + CalculatingDecimalNumbers() + Case "5" '작동하는 계산기 + Console.Clear() + WorkingCalculator() + Case "6" 'Do While 루프 사용 + Console.Clear() + UsingDoWhileLoops() + Case "7" 'For While 루프 사용 + Console.Clear() + UsingForLoops() + Case "8" '조건문 + Console.Clear() + ConditionalStatement() + Case "9" 'If/Else 문 + Console.Clear() + IfElseStatement() '음료 선택 + Case "50" '정보 메시지 상자 + Console.Clear() + Console.Title = ("Learn X in Y Minutes :: About") + MsgBox("This tutorial is by Brian Martin (@BrianMartinn") + Console.Clear() + Main() + Console.ReadLine() + + End Select + End Sub + + '하나 - 나중에 빌드할 때 위의 내비게이션을 돕기 위해 숫자를 사용하고 있습니다. + + '프로그램의 다른 섹션을 분리하기 위해 private sub를 사용합니다. + Private Sub HelloWorldOutput() + '콘솔 애플리케이션 제목 + Console.Title = "Hello World Output | Learn X in Y Minutes" + '출력을 인쇄하려면 Console.Write("") 또는 Console.WriteLine("")을 사용하십시오. + '그 다음에 Console.Read() 또는 Console.Readline()이 옵니다. + 'Console.ReadLine()은 출력을 콘솔에 인쇄합니다. + Console.WriteLine("Hello World") + Console.ReadLine() + End Sub + + '둘 + Private Sub HelloWorldInput() + Console.Title = "Hello World YourName | Learn X in Y Minutes" + '변수 + '사용자가 입력한 데이터는 저장해야 합니다. + '변수도 Dim으로 시작하고 As VariableType으로 끝납니다. + + '이 튜토리얼에서는 사용자의 이름을 알고 프로그램이 + '말한 내용에 응답하도록 하려고 합니다. + Dim username As String + '문자열은 텍스트 기반 변수이므로 문자열을 사용합니다. + Console.WriteLine("Hello, What is your name? ") '사용자에게 이름을 묻습니다. + username = Console.ReadLine() '사용자 이름을 저장합니다. + Console.WriteLine("Hello " + username) '출력은 Hello '사용자 이름'입니다. + Console.ReadLine() '사용자가 읽을 수 있도록 실행을 일시 중지합니다. + + '위의 내용은 질문을 한 다음 답변을 인쇄합니다. + '다른 변수에는 Integer가 포함되며 정수에는 Integer를 사용합니다. + End Sub + + '셋 + Private Sub CalculatingWholeNumbers() + Console.Title = "Calculating Whole Numbers | Learn X in Y Minutes" + Console.Write("First number: ") '정수를 입력하십시오. 1, 2, 50, 104 등 + Dim a As Integer = Console.ReadLine() + Console.Write("Second number: ") '두 번째 정수를 입력하십시오. + Dim b As Integer = Console.ReadLine() + Dim c As Integer = a + b + Console.WriteLine(c) + Console.ReadLine() + '위는 간단한 계산기입니다. + End Sub + + '넷 + Private Sub CalculatingDecimalNumbers() + Console.Title = "Calculating with Double | Learn X in Y Minutes" + '물론 소수를 더할 수 있기를 원할 것입니다. + '따라서 위의 내용을 Integer에서 Double로 변경할 수 있습니다. + + '부동 소수점 숫자를 입력하십시오. 1.2, 2.4, 50.1, 104.9 등 + Console.Write("First number: ") + Dim a As Double = Console.ReadLine + Console.Write("Second number: ") '두 번째 부동 소수점 숫자를 입력하십시오. + Dim b As Double = Console.ReadLine + Dim c As Double = a + b + Console.WriteLine(c) + Console.ReadLine() + '따라서 위의 프로그램은 1.1 - 2.2를 더할 수 있습니다. + End Sub + + '다섯 + Private Sub WorkingCalculator() + Console.Title = "The Working Calculator | Learn X in Y Minutes" + '그러나 계산기가 빼기, 나누기, 곱하기 및 + '더하기를 원한다면. + '위의 내용을 다시 복사하여 붙여넣으십시오. + Console.Write("First number: ") + Dim a As Double = Console.ReadLine + Console.Write("Second number: ") '두 번째 부동 소수점 숫자를 입력하십시오. + Dim b As Double = Console.ReadLine + Dim c As Double = a + b + Dim d As Double = a * b + Dim e As Double = a - b + Dim f As Double = a / b + + '아래 줄을 추가하여 빼기, + '곱하기 및 a와 b 값을 나눌 수 있습니다. + Console.Write(a.ToString() + " + " + b.ToString()) + '답변을 왼쪽으로 3칸 채우고 싶습니다. + Console.WriteLine(" = " + c.ToString.PadLeft(3)) + Console.Write(a.ToString() + " * " + b.ToString()) + Console.WriteLine(" = " + d.ToString.PadLeft(3)) + Console.Write(a.ToString() + " - " + b.ToString()) + Console.WriteLine(" = " + e.ToString.PadLeft(3)) + Console.Write(a.ToString() + " / " + b.ToString()) + Console.WriteLine(" = " + f.ToString.PadLeft(3)) + Console.ReadLine() + + End Sub + + '여섯 + Private Sub UsingDoWhileLoops() + '이전 private sub와 마찬가지로 + '이번에는 사용자가 계속할지 묻습니다(예 또는 아니요?). + '사용자가 프로그램을 두 번 이상 사용하기를 원하는지 + '확실하지 않으므로 Do While 루프를 사용하고 있습니다. + Console.Title = "UsingDoWhileLoops | Learn X in Y Minutes" + Dim answer As String '답변이 텍스트이므로 "String" 변수를 사용합니다. + Do '프로그램을 시작합니다. + Console.Write("First number: ") + Dim a As Double = Console.ReadLine + Console.Write("Second number: ") + Dim b As Double = Console.ReadLine + Dim c As Double = a + b + Dim d As Double = a * b + Dim e As Double = a - b + Dim f As Double = a / b + + Console.Write(a.ToString() + " + " + b.ToString()) + Console.WriteLine(" = " + c.ToString.PadLeft(3)) + Console.Write(a.ToString() + " * " + b.ToString()) + Console.WriteLine(" = " + d.ToString.PadLeft(3)) + Console.Write(a.ToString() + " - " + b.ToString()) + Console.WriteLine(" = " + e.ToString.PadLeft(3)) + Console.Write(a.ToString() + " / " + b.ToString()) + Console.WriteLine(" = " + f.ToString.PadLeft(3)) + Console.ReadLine() + '질문을 합니다. 사용자가 계속하기를 원합니까? 안타깝게도 + '대소문자를 구분합니다. + Console.Write("Would you like to continue? (yes / no) ") + '프로그램이 변수를 가져와 인쇄하고 다시 시작합니다. + answer = Console.ReadLine + '이 경우 변수가 작동하려면 "yes" 명령이 필요합니다. + Loop While answer = "yes" + + End Sub + + '일곱 + Private Sub UsingForLoops() + '때로는 프로그램이 한 번만 실행하면 됩니다. + '이 프로그램에서는 10부터 카운트다운합니다. + + Console.Title = "Using For Loops | Learn X in Y Minutes" + '변수를 선언하고 Step -1, + 'Step -2, Step -3 등에서 카운트다운할 숫자를 선언합니다. + For i As Integer = 10 To 0 Step -1 + Console.WriteLine(i.ToString) '카운터 값 인쇄 + Next i '새 값 계산 + Console.WriteLine("Start") '프로그램을 시작합시다!! + Console.ReadLine() 'POW!! - 아마도 그때 좀 흥분했나 봅니다 :) + End Sub + + '여덟 + Private Sub ConditionalStatement() + Console.Title = "Conditional Statements | Learn X in Y Minutes" + Dim userName As String + Console.WriteLine("Hello, What is your name? ") '사용자에게 이름을 묻습니다. + userName = Console.ReadLine() '사용자 이름을 저장합니다. + If userName = "Adam" Then + Console.WriteLine("Hello Adam") + Console.WriteLine("Thanks for creating this useful site") + Console.ReadLine() + Else + Console.WriteLine("Hello " + userName) + Console.WriteLine("Have you checked out www.learnxinyminutes.com") + Console.ReadLine() '위 문을 끝내고 인쇄합니다. + End If + End Sub + + '아홉 + Private Sub IfElseStatement() + Console.Title = "If / Else Statement | Learn X in Y Minutes" + '때로는 두 가지 이상의 대안을 고려하는 것이 중요합니다. + '때로는 다른 좋은 대안이 몇 가지 있습니다. + '이 경우 둘 이상의 if 문이 필요합니다. + 'if 문은 사용자가 코드를 입력하는 자동 판매기에 적합합니다. + 'A1, A2, A3 등을 사용하여 항목을 선택합니다. + '모든 선택은 단일 if 블록으로 결합할 수 있습니다. + + Dim selection As String '선택 변수 선언 + Console.WriteLine("Please select a product form our lovely vending machine.") + Console.WriteLine("A1. for 7Up") + Console.WriteLine("A2. for Fanta") + Console.WriteLine("A3. for Dr. Pepper") + Console.WriteLine("A4. for Diet Coke") + + selection = Console.ReadLine() '사용자로부터 선택 저장 + If selection = "A1" Then + Console.WriteLine("7up") + ElseIf selection = "A2" Then + Console.WriteLine("fanta") + ElseIf selection = "A3" Then + Console.WriteLine("dr. pepper") + ElseIf selection = "A4" Then + Console.WriteLine("diet coke") + Else + Console.WriteLine("Sorry, I don't have any " + selection) + End If + Console.ReadLine() + + End Sub + +End Module +``` diff --git a/ko/wasm.md b/ko/wasm.md new file mode 100644 index 0000000000..8e5064a363 --- /dev/null +++ b/ko/wasm.md @@ -0,0 +1,309 @@ +--- +name: WebAssembly +filename: learn-wasm.wast +contributors: + - ["Dean Shaff", "http://dean-shaff.github.io"] +--- + +```wast +;; learn-wasm.wast + +(module + ;; WebAssembly에서는 모든 것이 모듈에 포함됩니다. 또한, 모든 것을 + ;; s-표현식으로 표현할 수 있습니다. 또는 "스택 머신" 구문이 있지만, + ;; 이는 Binaryen 중간 표현(IR) 구문과 호환되지 않습니다. + + ;; Binaryen IR 형식은 WebAssembly 텍스트 형식과 *대부분* 호환됩니다. + ;; 몇 가지 작은 차이점이 있습니다: + ;; local_set -> local.set + ;; local_get -> local.get + + ;; 코드를 함수 안에 포함해야 합니다. + + ;; 데이터 유형 + (func $data_types + ;; WebAssembly에는 네 가지 유형만 있습니다: + ;; i32 - 32비트 정수 + ;; i64 - 64비트 정수 (JavaScript에서는 지원되지 않음) + ;; f32 - 32비트 부동 소수점 + ;; f64 - 64비트 부동 소수점 + + ;; "local" 키워드로 지역 변수를 선언할 수 있습니다. + ;; 함수 내에서 어떤 작업을 시작하기 전에 모든 변수를 선언해야 합니다. + + (local $int_32 i32) + (local $int_64 i64) + (local $float_32 f32) + (local $float_64 f64) + + ;; 이 값들은 초기화되지 않은 상태로 남아 있습니다. + ;; 값을 설정하려면 .const를 사용할 수 있습니다: + + (local.set $int_32 (i32.const 16)) + (local.set $int_64 (i64.const 128)) + (local.set $float_32 (f32.const 3.14)) + (local.set $float_64 (f64.const 1.28)) + ) + + ;; 기본 연산 + (func $basic_operations + + ;; WebAssembly에서는 수학을 하거나 변수 값을 가져오는 것을 + ;; 포함하여 모든 것이 s-표현식입니다. + + (local $add_result i32) + (local $mult_result f64) + + (local.set $add_result (i32.add (i32.const 2) (i32.const 4))) + ;; add_result의 값은 이제 6입니다! + + ;; 각 연산에 올바른 데이터 유형을 사용해야 합니다: + ;; (local.set $mult_result (f32.mul (f32.const 2.0) (f32.const 4.0))) ;; 틀림! mult_result는 f64입니다! + (local.set $mult_result (f64.mul (f64.const 2.0) (f64.const 4.0))) + + ;; WebAssembly에는 기본 수학 및 비트 시프트와 같은 일부 내장 연산이 있습니다. + ;; 특히 삼각 함수는 내장되어 있지 않습니다. + ;; 이러한 함수에 액세스하려면 다음 중 하나를 수행해야 합니다. + ;; - 직접 구현 (권장하지 않음) + ;; - 다른 곳에서 가져오기 (나중에) + ) + + ;; 함수 + ;; `param` 키워드로 인수를 지정하고 `result` 키워드로 반환 값을 + ;; 지정합니다. + ;; 스택의 현재 값은 함수의 반환 값입니다. + + ;; `call` 키워드로 정의한 다른 함수를 호출할 수 있습니다. + + (func $get_16 (result i32) + (i32.const 16) + ) + + (func $add (param $param0 i32) (param $param1 i32) (result i32) + (i32.add + (local.get $param0) + (local.get $param1) + ) + ) + + (func $double_16 (result i32) + (i32.mul + (i32.const 2) + (call $get_16)) + ) + + ;; 지금까지는 아무것도 인쇄할 수 없었고, + ;; 상위 수준의 수학 함수(pow, exp 또는 삼각 함수)에도 액세스할 수 없었습니다. + ;; 또한 JavaScript에서 WASM 함수를 사용할 수 없었습니다! + ;; WebAssembly에 이러한 함수를 가져오는 방법은 + ;; Node.js 또는 브라우저 환경에 따라 다르게 보입니다. + + ;; Node.js에 있는 경우 두 단계를 수행해야 합니다. 먼저 WASM 텍스트 + ;; 표현을 실제 웹어셈블리로 변환해야 합니다. Binyaren을 사용하는 경우 + ;; 다음과 같은 명령으로 수행할 수 있습니다: + + ;; wasm-as learn-wasm.wast -o learn-wasm.wasm + + ;; 다음과 같은 명령으로 해당 파일에 Binaryen 최적화를 적용할 수 있습니다: + + ;; wasm-opt learn-wasm.wasm -o learn-wasm.opt.wasm -O3 --rse + + ;; 컴파일된 WebAssembly를 사용하여 이제 Node.js에 로드할 수 있습니다: + ;; const fs = require('fs') + ;; const instantiate = async function (inFilePath, _importObject) { + ;; var importObject = { + ;; console: { + ;; log: (x) => console.log(x), + ;; }, + ;; math: { + ;; cos: (x) => Math.cos(x), + ;; } + ;; } + ;; importObject = Object.assign(importObject, _importObject) + ;; + ;; var buffer = fs.readFileSync(inFilePath) + ;; var module = await WebAssembly.compile(buffer) + ;; var instance = await WebAssembly.instantiate(module, importObject) + ;; return instance.exports + ;; } + ;; + ;; const main = function () { + ;; var wasmExports = await instantiate('learn-wasm.wasm') + ;; wasmExports.print_args(1, 0) + ;; } + + ;; 다음 스니펫은 JavaScript instantiate async 함수에서 정의한 + ;; importObject에서 함수를 가져온 다음, Node.js에서 호출할 수 있는 + ;; "print_args" 함수를 내보냅니다. + + (import "console" "log" (func $print_i32 (param i32))) + (import "math" "cos" (func $cos (param f64) (result f64))) + + (func $print_args (param $arg0 i32) (param $arg1 i32) + (call $print_i32 (local.get $arg0)) + (call $print_i32 (local.get $arg1)) + ) + (export "print_args" (func $print_args)) + + ;; WebAssembly 메모리에서 데이터 로드. + ;; JavaScript 배열에 코사인 함수를 적용하고 싶다고 가정해 봅시다. + ;; 할당된 배열에 액세스하고 반복할 수 있어야 합니다. + ;; 이 예에서는 입력 배열을 제자리에서 수정합니다. + ;; f64.load 및 f64.store는 메모리에서 숫자의 위치를 *바이트* 단위로 예상합니다. + ;; 배열의 세 번째 요소에 액세스하려면 + ;; (i32.mul (i32.const 8) (i32.const 2))와 같은 것을 f64.store 함수에 + ;; 전달해야 합니다. + + ;; JavaScript에서는 다음과 같이 `apply_cos64`를 호출합니다. + ;; (이전의 instantiate 함수 사용): + ;; + ;; const main = function () { + ;; var wasm = await instantiate('learn-wasm.wasm') + ;; var n = 100 + ;; const memory = new Float64Array(wasm.memory.buffer, 0, n) + ;; for (var i=0; ia = a; + (i32.store + (get_local $sum_struct_ptr) + (get_local $var$a) + ) + + ;; c// sum_struct_ptr->b = b; + (i32.store offset=4 + (get_local $sum_struct_ptr) + (get_local $var$b) + ) + ) + + (func $sum_local (result i32) + (local $var$sum_struct$a i32) + (local $var$sum_struct$b i32) + (local $local_memstack_ptr i32) + + ;; memstack 공간 예약 + (i32.sub + (get_global $memstack_ptr) + (i32.const 8) + ) + tee_local $local_memstack_ptr ;; tee는 주어진 값을 저장하고 반환합니다. + set_global $memstack_ptr + + ;; 함수를 호출하여 결과를 memstack에 저장합니다. + (call $sum_struct_create + ((;$sum_struct_ptr=;) get_local $local_memstack_ptr) + ((;$var$a=;) i32.const 40) + ((;$var$b=;) i32.const 2) + ) + + ;; 구조체에서 값 검색 + (set_local $var$sum_struct$a + (i32.load offset=0 (get_local $local_memstack_ptr)) + ) + (set_local $var$sum_struct$b + (i32.load offset=4 (get_local $local_memstack_ptr)) + ) + + ;; memstack 공간 예약 해제 + (set_global $memstack_ptr + (i32.add + (get_local $local_memstack_ptr) + (i32.const 8) + ) + ) + + (i32.add + (get_local $var$sum_struct$a) + (get_local $var$sum_struct$b) + ) + ) + (export "sum_local" (func $sum_local)) +) +``` diff --git a/ko/wikitext.md b/ko/wikitext.md new file mode 100644 index 0000000000..70afd68b81 --- /dev/null +++ b/ko/wikitext.md @@ -0,0 +1,269 @@ +--- +name: Wikitext +contributors: + - ["Yuxi Liu", "https://github.com/yuxiliu1995/"] +filename: wikitext.md +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +위키는 온라인에서 공동으로 편집하는 하이퍼텍스트 출판물이며, 가장 유명한 것은 위키백과입니다. 위키텍스트는 위키에서 사용하는 마크업 언어입니다. 구문은 마크다운과 HTML의 혼합과 유사합니다. + +## 구문 + +`` + +| 위키텍스트 | 동등한 마크다운 | 효과 | +| ---- | ---- | ---- | +| `''기울임꼴''` | `*기울임꼴*` | *기울임꼴* | +| `'''굵게'''` | `**굵게**` | **굵게** | +| `'''''둘 다'''''` | `***둘 다***` | ***둘 다*** | +| `밑줄` | `밑줄` | 밑줄 | +| `렌더링하지 않음` | 해당 없음 | `렌더링하지 않음` | +| `인라인 코드 조각` | \`인라인 코드 조각\` | `인라인 코드 조각` | +| `----` | `----` | 수평선 | +| `취소선` | `~~취소선~~` | ~~취소선~~ | + +섹션 제목은 `=`로 묶습니다. `= 한 개의 등호 =`에서 `====== 여섯 개의 등호 ======`까지 있습니다. 이것은 마크다운의 해시태그 제목, `# 한 개의 해시태그`에서 `###### 여섯 개의 해시태그`까지와 동일합니다. 왜 둘 다 여섯 개일까요? HTML에 `

`에서 `

`까지 여섯 단계의 제목이 있기 때문이라고 생각합니다. + +`= 한 개의 등호 =` 제목은 실제로는 페이지의 제목에 해당하며, 따라서 페이지 내에서 실제로 사용할 수 없습니다. 결과적으로, 가장 적은 수의 등호는 `== 두 개의 등호 ==`입니다. + +아래 첨자와 위 첨자는 `x1` 및 `x1`으로 쓸 수 있습니다. 또는 `` 태그로 쓸 수 있습니다(아래 참조). `작게` 및 `크게` 텍스트는 거의 사용되지 않습니다. + +```wikitext +콜론은 들여쓰기를 허용합니다. + :각 콜론은 세 문자 너비의 들여쓰기를 만듭니다. + ::그리고 중첩될 수 있습니다. +``` + +`*` 번호 없는 목록은 `*`로 시작하고, 번호 있는 목록은 `#`로 시작합니다.
+  `**` 목록은 중첩될 수 있습니다
+    `***` 임의의 많은 수준에 대해. + +표의 구문은 [매우 복잡합니다](https://en.wikipedia.org/wiki/Help:Table). [간단한 표](https://en.wikipedia.org/wiki/Help:Basic_table_markup) 중 가장 간단한 것은 다음과 같습니다: + +```wikitext +{| class="wikitable" +|+ +! 열 제목 A +! 열 제목 B +|- +| 셀 A1 +| 셀 B1 +|- +| 셀 A2 +| 셀 B2 +|- +| ... +| ... +|} +``` + +다음과 같이 렌더링됩니다. + +| **열 제목 A** | **열 제목 B** | +|---|---| +| 셀 A1 | 셀 B1 | +| 셀 A2 | 셀 B2 | + +위키텍스트 표의 개행은 의미가 있다는 점에 유의하십시오. 위의 단일 개행을 삭제하면 렌더링된 표의 모양이 완전히 바뀝니다. + +`[[File:Image.png|thumb|right|Image caption]]`으로 이미지, 오디오, 비디오 또는 기타 미디어 형식을 삽입할 수 있습니다. 모든 미디어 파일은 [Wikimedia Commons](https://commons.wikimedia.org/wiki/Main_Page)에 호스팅되어야 합니다. + +HTML과 유사한 태그로 인용문을 삽입할 수 있습니다. + +```wikitext +
+

인용문 텍스트.

+

이름, 출처, 참조

+
+``` + +또는 [템플릿](#템플릿) + +```wikitext +{{Quote|text=인용문 텍스트.|title=제목|author=저자|source=출판물 내 위치}} +``` + +"[줄 바꿈 없는 공백](https://en.wikipedia.org/wiki/Non-breaking_space)"은 "400km/h"의 공백과 같이 줄 바꿈으로 분리되어서는 안 되는 공백입니다. 이것은 `400&nbsp;km/h`로 작성됩니다. + +추가 공백은 `pad` 태그로 지정할 수 있습니다. 예를 들어, `{{pad|4.0em}}`은 길이 4.0 [em-대시](https://en.wikipedia.org/wiki/Dash#Em_dash)의 공백입니다. + +더 긴 코드 블록은 다음과 같이 할 수 있습니다. + +```wikitext + +#include +int m2 (int ax, char *p_ax) { + std::cout <<"Hello World!"; + return 0; +} +``` + +다음과 같이 렌더링됩니다. + +```cpp +#include +int m2 (int ax, char *p_ax) { + std::cout <<"Hello World!"; + return 0; +} +``` + +## 링크 + +기본 `[[링크]]`는 이중 대괄호로 수행됩니다. + +`|` 기호는 `[[실제 페이지 제목|다른 텍스트]]`를 표시할 수 있습니다. + +`#` 기호는 `[[Frog#Locomotion]]` 또는 `[[Frog#Locomotion|개구리의 이동]]`과 같이 텍스트 내 섹션에 연결할 수 있습니다. + +단어가 링크로 중단되면 링크에 "혼합"됩니다. 예를 들어, `[[copy edit]]ors`는 [copy editors](https://en.wikipedia.org/wiki/copy_edit)로 렌더링됩니다. + +이 동작을 억제하려면 ``를 사용하십시오. 예를 들어, `[[micro-]]second`는 [micro-](https://en.wikipedia.org/wiki/micro-)second로 렌더링됩니다. + +외부 링크에는 세 가지 종류가 있습니다. 세 번째 종류가 선호됩니다: + +| 위키텍스트 | 렌더링 결과 | +|----|----| +| `https://www.wikipedia.org` | [https://www.wikipedia.org](https://www.wikipedia.org) | +| `[https://www.wikipedia.org]` | [[1]](https://www.wikipedia.org) | +| `[https://www.wikipedia.org Wikipedia]` | [Wikipedia](https://www.wikipedia.org) | + +## 템플릿 + +템플릿은 위키텍스트용 매크로이며, `{{템플릿 이름|속성=값|...}}`처럼 보입니다. 수천 개의 템플릿이 있지만 일반적으로 사용되는 것은 몇 가지뿐입니다. + +가장 (악)명 높은 것은 \[출처 필요\]`{{cn}}` 템플릿입니다. `{{cn}}`은 `{{citation needed}}`와 동의어이며, 한 템플릿이 여러 이름을 가질 수 있다는 점에 유의하십시오. + +`{{reflist}}`는 일반적으로 페이지 끝에 배치되어 페이지에서 사용된 참조 목록을 생성합니다. + +`infobox` 템플릿은 이름에서 알 수 있듯이 정보를 포함하는 상자용 템플릿입니다. 일반적으로 각 페이지에는 위쪽에 하나, 아래쪽에 하나, 최대 두 개의 정보 상자가 포함됩니다. 특히 상세한 페이지의 경우 두 개 이상 있을 수 있습니다. + +위쪽 정보 상자는 일반적으로 표 형식 정보를 간결하게 표시하는 데 사용됩니다. 전기, 지리적 위치 등에 일반적입니다. 예를 들어, [오일러](https://en.wikipedia.org/wiki/Leonhard_Euler)의 위쪽 정보 상자는 다음과 같습니다: + +```wikitext +{{Infobox scientist +| name = Leonhard Euler +| image = Leonhard Euler.jpg +| caption = Portrait by [[Jakob Emanuel Handmann]], 1753 +| birth_date = {{birth date|df=y|1707|4|15}} +| birth_place = [[Basel]], [[Swiss Confederacy]] +| death_date = {{nowrap|{{death date and age|df=y|1783|9|18|1707|4|15}}}} {{awrap|{{bracket|[[Adoption of the Gregorian calendar#Adoption in Eastern Europe|OS]]: 7 September 1783}}}} +... +}} +``` + +아래쪽 정보 상자는 일반적으로 관련 링크의 선별된 표를 표시하는 데 사용됩니다. 예를 들어, [오일러-라그랑주 방정식](https://en.wikipedia.org/wiki/Euler%E2%80%93Lagrange_equation)의 아래쪽 정보 상자는 `{{Leonhard Euler}}`뿐이며, 오일러의 이름을 딴 많은 것들에 대한 링크가 포함된 상자를 표시합니다. + +`~~~~`는 토론 페이지에 서명하는 데 사용되며, `Username (talk) 10:50, 12 June 2023 (UTC)`와 같이 확장됩니다. + +### 수학 + +`` 태그는 `$`처럼 $\LaTeX$를 인라인으로 렌더링하고, ``은 `$$`처럼 별도의 줄에 렌더링합니다. + +`E = mc^2`는 $E = mc^2$로 렌더링됩니다. + +``는 $$E = mc^2$$로 렌더링됩니다. + +[HTML 렌더링](https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style/Mathematics#Using_HTML)을 사용하거나 [일반 유니코드](https://en.wikipedia.org/wiki/Mathematical_operators_and_symbols_in_Unicode)를 사용하여 수학을 포함할 수도 있습니다. 이것들은 덜 유연하지만 이전 브라우저와 더 호환됩니다. 또한 위키백과 구문의 일부는 섹션 제목이나 일부 템플릿에서와 같이 ``와 호환되지 않으므로 이러한 경우 HTML 또는 유니코드를 사용해야 합니다. + +정리와 증명은 상자로 묶고 이름을 지정할 수 있습니다: + +```wikitext +{{Math theorem +|name=피타고라스 정리 +|note=피타고라스, 기원전 500년대 +|math_statement=직각삼각형의 세 변의 길이를 a, b, c라고 하면 +a^2 + b^2 = c^2 +}} + +{{Math proof +|title=닮은 삼각형에 의한 증명 +|proof=점 C에서 변 AB에 수선을 내립니다. 이제 비례 관계로 논증합니다. \blacksquare +}} +``` + +## 참조 + +참조는 위키백과의 근간입니다 `{{출처 필요}}`. 일반적으로 인용에는 두 가지 방법이 있습니다. + +| 유형 | 인라인 인용 | 확장 인용 | +| ---- | ---- | ---- | +| 목적 | 특정 주장을 뒷받침합니다. | 전체 페이지에 대한 일반 참조 작업을 제공합니다. | +| 위치 | 지원되는 주장 바로 뒤. | `== 참조 ==` 섹션. | +| 모양 | _f_의 해석적 연속.[\[6\]](#6) | Abramowitz, Milton; Stegun, Irene A., eds. (1972). ["Chapter 6"](http://www.math.sfu.ca/~cbm/aands/page_253.htm)... | +| 구문 | `{{cite book\|...}}` | `{{cite book\|...}}` | + +확장 인용은 `` 태그가 없는 인라인 인용이므로 인라인 인용만 설명합니다. + +가장 기본적인 형태는 `저자, 제목, 날짜, [url](https://example.com/) 등`과 같은 일반 텍스트 인용입니다. + +일반적으로 `{{cite web|url=https://example.com/|title=예제|date=2001|access-date=2023}}`와 같은 템플릿 인용을 사용해야 합니다. 인용 템플릿에는 [`cite web`](https://en.wikipedia.org/wiki/Template:Cite_web), [`cite journal`](https://en.wikipedia.org/wiki/Template:Cite_journal), [`cite book`](https://en.wikipedia.org/wiki/Template:Cite_book)의 세 가지 형태가 있습니다. + +인용은 `...`로 이름을 지정할 수 있습니다. 그런 다음 ``로 호출할 수 있습니다. 인스턴스 `...`는 `` 앞이나 뒤에 올 수 있습니다. 어떤 순서든 동일한 페이지로 렌더링됩니다. + +## 일반적인 위키백과 페이지 + +```wikitext +{{짧은 설명|페이지의 한 문장 요약}} + +{{위쪽 정보 상자 +|infobox_data_1=... +|... +}} + +[[File:X의 이미지.png|thumb|right|이미지 캡션]] + +'''X''' 개념은 일반적으로 굵게 표시됩니다. 이제 X 개념을 정의합니다. 비전문가 페이지의 경우 이 섹션은 평이한 언어로 작성되어야 하며, 전문 용어는 인라인으로 정의됩니다. 일부 [[링크]]가 도움이 될 것입니다. + + +== 소개 == + +여기서는 일반적으로 표기법을 설정하고, 역사를 개괄하며, 기타 등등을 합니다. 자세한 내용은 다음 섹션에서 다룹니다. + +각주는 인라인 참조와 별도로 번호가 매겨집니다.{{NoteTag|note=각주 텍스트.}} + +== Y와의 관계 == +{{Main|Y}} +{{See also|다른 페이지}} + +X와 Y의 관계에 대한 무언가. + +== 함께 보기 == +* [[매우 관련 있는 링크]] +* [[덜 관련 있는 링크]] + +== 외부 링크 == +* [https://example.com/ 외부 링크 1]: 외부 링크에 있는 내용 요약. + +== 각주 == + +{{Notelist}} + +== 참조 == + +{{Reflist|30em}} + + +{{Refbegin|30em}} +* {{cite book|title=책 제목|date=2001|chapter=1장|...}} +* ... + +== 더 읽을거리 == +* ... +* ... + +{{아래쪽 정보 상자}} + +[[Category:기사가 속한 첫 번째 카테고리]] +[[Category:기사가 속한 첫 번째 카테고리]] +[[Category:허용되는 카테고리 수에는 제한이 없습니다]] +``` + +## 더 읽을거리 + +* [위키백과의 스타일 매뉴얼](https://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style) +* [위키텍스트 치트 시트](https://en.wikipedia.org/wiki/Help:Cheatsheet) +* [위키텍스트, 전체 참조](https://en.wikipedia.org/wiki/Help:Wikitext). +* [표, 전체 참조](https://en.wikipedia.org/wiki/Help:Table#Simple_straightforward_tables) diff --git a/ko/wolfram.md b/ko/wolfram.md new file mode 100644 index 0000000000..5153a54da6 --- /dev/null +++ b/ko/wolfram.md @@ -0,0 +1,140 @@ +--- +name: Wolfram +contributors: + - ["hyphz", "http://github.com/hyphz/"] +filename: learnwolfram.nb +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +Wolfram 언어는 원래 Mathematica에서 사용되던 기본 언어이지만, 지금은 여러 컨텍스트에서 사용할 수 있습니다. + +Wolfram 언어에는 여러 인터페이스가 있습니다: + +* 라즈베리 파이의 명령줄 커널 인터페이스(_The Wolfram Language_라고도 함)는 대화형으로 실행되며 그래픽 입력을 생성할 수 없습니다. +* _Mathematica_는 대화형 Wolfram이 내장된 풍부한 텍스트/수학 편집기입니다: "코드 셀"에서 shift + Return을 누르면 결과가 포함된 출력 셀이 생성되며, 이는 동적이 아닙니다. +* _Wolfram Workbench_는 Wolfram 언어 백엔드에 연결된 Eclipse입니다. + +이 예제의 코드는 모든 인터페이스에 입력하고 Wolfram Workbench로 편집할 수 있습니다. 파일에 셀 서식 정보가 포함되어 있지 않아(텍스트로 읽기에 매우 지저분하게 만듦) Mathematica로 직접 로드하는 것이 어색할 수 있습니다. 보거나 편집할 수는 있지만 일부 설정이 필요할 수 있습니다. + +```mathematica +(* 이것은 주석입니다 *) + +(* Mathematica에서는 이러한 주석 대신 텍스트 셀을 만들어 + 코드를 멋지게 조판된 텍스트와 이미지로 주석 처리할 수 있습니다 *) + +(* 표현식을 입력하면 결과가 반환됩니다 *) +2*2 (* 4 *) +5+8 (* 13 *) + +(* 함수 호출 *) +(* 참고, 함수 이름(및 다른 모든 것)은 대소문자를 구분합니다 *) +Sin[Pi/2] (* 1 *) + +(* 매개변수 하나를 사용하는 함수 호출의 대체 구문 *) +Sin@(Pi/2) (* 1 *) +(Pi/2) // Sin (* 1 *) + +(* WL의 모든 구문은 함수 호출과 동등한 형태를 가집니다 *) +Times[2, 2] (* 4 *) +Plus[5, 8] (* 13 *) + +(* 변수를 처음 사용하면 정의되고 전역 변수가 됩니다 *) +x = 5 (* 5 *) +x == 5 (* True, C 스타일 할당 및 동등성 테스트 *) +x (* 5 *) +x = x + 5 (* 10 *) +x (* 10 *) +Set[x, 20] (* 모든 것이 함수와 동등하다고 말했을 때 농담이 아니었습니다 *) +x (* 20 *) + +(* WL은 컴퓨터 대수 시스템을 기반으로 하므로, *) +(* 정의되지 않은 변수를 사용해도 괜찮습니다. 평가를 방해할 뿐입니다. *) +cow + 5 (* 5 + cow, cow는 정의되지 않았으므로 더 이상 평가할 수 없습니다 *) +cow + 5 + 10 (* 15 + cow, 가능한 것은 평가합니다 *) +% (* 15 + cow, %는 마지막 반환값을 가져옵니다 *) +% - cow (* 15, 정의되지 않은 변수 cow가 상쇄됨 *) +moo = cow + 5 (* 주의, moo는 이제 숫자가 아닌 표현식을 담고 있습니다! *) + +(* 함수 정의 *) +Double[x_] := x * 2 (* 참고: RHS의 즉시 평가를 막기 위한 := + 그리고 x 뒤의 _는 패턴 매칭 제약 조건이 없음을 나타냅니다 *) +Double[10] (* 20 *) +Double[Sin[Pi/2]] (* 2 *) +Double @ Sin @ (Pi/2) (* 2, @-구문은 닫는 괄호의 연속을 피합니다 *) +(Pi/2) // Sin // Double(* 2, //-구문은 함수를 실행 순서대로 나열합니다 *) + +(* 명령형 스타일 프로그래밍을 위해 ;를 사용하여 문장을 구분합니다 *) +(* LHS의 모든 출력을 버리고 RHS를 실행합니다 *) +MyFirst[] := (Print@"Hello"; Print@"World") (* 참고: 바깥쪽 괄호가 중요합니다 + ;의 우선순위는 :=보다 낮습니다 *) +MyFirst[] (* Hello World *) + +(* C-스타일 For 루프 *) +PrintTo[x_] := For[y=0, y 2, "Red" -> 1|> (* 연관 배열 생성 *) +myHash[["Green"]] (* 2, 사용 *) +myHash[["Green"]] := 5 (* 5, 업데이트 *) +myHash[["Puce"]] := 3.5 (* 3.5, 확장 *) +KeyDropFrom[myHash, "Green"] (* Green 키 제거 *) +Keys[myHash] (* {Red, Puce} *) +Values[myHash] (* {1, 3.5} *) + +(* 그리고 Wolfram 데모에서 이것을 보여주지 않을 수 없습니다 *) +Manipulate[y^2, {y, 0, 20}] (* y^2를 표시하고 슬라이더로 y를 0-20 사이에서 + 조정할 수 있는 반응형 사용자 인터페이스를 반환합니다. + 그래픽 프론트엔드에서만 작동합니다 *) +``` + +## 더 읽을거리 + +* [Wolfram 언어 문서 센터](http://reference.wolfram.com/language/) diff --git a/ko/zfs.md b/ko/zfs.md new file mode 100644 index 0000000000..652ab8d463 --- /dev/null +++ b/ko/zfs.md @@ -0,0 +1,452 @@ +--- +category: tool +name: ZFS +contributors: + - ["sarlalian", "http://github.com/sarlalian"] + - ["81reap", "https://github.com/81reap"] + - ["A1EF", "https://github.com/A1EF"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +filename: LearnZfs.txt +--- + +[ZFS](http://open-zfs.org/wiki/Main_Page)는 스토리지 스택을 재고한 것으로, 전통적인 파일 시스템과 볼륨 관리자를 하나의 통합된 도구로 결합한 것입니다. ZFS는 기존의 스토리지 시스템과 구별되는 몇 가지 고유한 용어를 가지고 있지만, 시스템 관리자를 위한 사용성에 초점을 맞춘 훌륭한 기능들을 제공합니다. + +## ZFS 개념 + +### 가상 장치 (Virtual De[1]vices) + +ZFS의 VDEV(가상 장치)는 RAID 장치와 유사하며, 중복성 및 성능 측면에서 다양한 이점을 제공합니다. 일반적으로 VDEV는 RAID 카드보다 더 나은 신뢰성과 안전성을 제공합니다. ZFS는 기본 디스크를 직접 관리하도록 설계되었기 때문에 ZFS와 함께 RAID 설정을 사용하는 것은 권장되지 않습니다. + +| VDEV 유형 | 유사 RAID | 참고 | +|-----------|----------------|---------------------------------------| +| 미러(Mirror) | RAID 1 | 중복성을 위해 n-way 미러링을 지원합니다. | +| raidz1 | RAID 5 | 단일 디스크 패리티로, 디스크 하나 장애를 허용합니다. | +| rai[1]dz2 | RAID 6 | 2-디스크 패리티로, 디스크 두 개 장애를 허용합니다. | +| raidz3 | - | 3-디스크 패리티로, 디스크 세 개 장애를 허용합니다. | +| 디스크(Disk) | - | VDEV 내의 단일 물리적 디스크를 나타냅니다. | +| 파일(File) | - | 파일 기반 VDEV로, 복잡성을 더하고 신뢰성을 떨어뜨려 운영 환경에는 권장되지 않습니다. | + +ZFS 스토리지 풀의 데이터는 모든 VDEV에 걸쳐 스트라이프됩니다. 더 많은 VDEV, 로그 또는 캐시를 추가하면 IOPS(초당 입출력 연산)가 증가하여 성능이 향상됩니다. 최적의 성능과 중복성을 위해 VDEV의 균형를 맞추는 것이 중요합니다. + +### 스토리지 풀 + +ZFS는 하위 레벨 스토리지 제공자(VDEV)에 대한 추상화로 스토리지 풀을 사용하여, 사용자에게 보이는 파일 시스템을 물리적 레이아웃과 분리할 수 있게 해줍니다. + +### ZFS 데이터셋 + +ZFS 데이터셋은 전통적인 파일 시스템과 유사하지만 훨씬 더 많은 기능을 가지고 있습니다. ZFS의 많은 장점을 제공합니다[1]. 데이터셋은 [COW(기록 중 복사)](https://en.wikipedia.org/wiki/Copy-on-write) 스냅샷, 할당량, 압축 및 중복 제거를 지원합니다[1]. + +### 제한 + +하나의 디렉터리는 최대 2^48개의 파일을 포함할 수 있으며, 각 파일은 최대 16엑사바이트까지 가능합니다. 단일 스토리지 풀은 최대 [1]256제타바이트(2^78)의 공간을 가질 수 있으며, 2^64개의 장치에 걸쳐 스트라이프될 수 있습니다. 단일 호스트는 2^64개의 스토리지 풀을 가질 수 있습니다. 이 제한은 엄청나게 큽니다. + +## 명령어 + +### 스토리지 풀 + +작업: + +* 목록 보기 +* 상태 확인 +* 제거 +* 속성 가져오기/설정하기 + +zpool 목록 보기 + +```bash +# raidz zpool 생성 +$ zpool create zroot raidz1 gpt/zfs0 gpt/zfs1 gpt/zfs2 + +# ZPool 목록 보기 +$ zpool list +NAME SIZE ALLOC FREE EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT +zroot 141G 106G 35.2G - 43% 75% 1.00x ONLINE - + +# 특정 zpool에 대한 상세 정보 보기 +$ zpool list -v zroot +NAME SIZE ALLOC FREE EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT +zroot 141G 106G 35.2G - 43% 75% 1.00x ONLINE - + gptid/c92a5ccf-a5bb-11e4-a77d-001b2172c655 141G 106G 35.2G - [2]43% 75% +``` + +zpool 상태 + +```bash +# zpool 상태 정보 가져오기 +$ zpool status + pool: zroot + state: ONLINE + scan: scrub repaired 0 in 2h51m with 0 errors[3] on Thu Oct 1 07:08:31 2015 +config: + + NAME STATE READ WRITE CKSUM + zroot ONLINE 0 0 [4] 0 + gptid/c92a5ccf-a5bb-11e4-a77d-001b2172c655 ONLINE 0 0 0 + +errors: No known data errors + +# 오류 수정을 위해 zpool 스크럽하기 +$ zpool scru[5]b zroot +$ zpool status -v zroot + pool: zroot + state: ONLINE + scan: scrub in progress since Thu Oct 15 16:59:14 2015 + 39.1M scanned out of 106G at 1.45M/s, 20h47m to go + 0 repaired, 0.04% done +config: + + NAME STATE READ WRITE CKSUM + zroot ONLINE 0 0 0 + gptid/c92a5ccf-a5bb-11e4-a77d-001b2172c655 ONLINE 0 0 0 + +errors: No known data errors +``` + +zpool 속성 + +```bash +# 풀 속성 가져오기. 속성은 사용자가 설정하거나 시스템이 제공할 수 있습니다. +$ zpool get all zroot +NAME PROPERTY VALUE SOURCE +zroot size 141G - +zroot capacity 75% - +zroot altroot - default +zroot health ONLINE - +... + +# zpool 속성 설정하기 +$ zpool set comment="내 자료 저장소" zroot +$ zpool get comment +NAME PROPERTY VALUE SOURCE +tank comment - default +zroot comment 내 자료 저장소 local +``` + +zpool 제거 + +```bash +$ zpool destroy test +``` + +### 데이터셋 + +작업: + +* 생성 +* 목록 보기 +* 이름 변경 +* 삭제 +* 속성 가져오기/설정하기 + +데이터셋 생성 + +```bash +# 데이터셋 생성 +$ zfs create zroot/root/data +$ mount | grep data +zroot/root/data on /data (zfs, local, nfsv4acls) + +# 자식 데이터셋 생성 +$ zfs create zroot/root/data/stuff +$ mount | grep data +zroot/root/data on /data (zfs, local, nfsv4acls) +zroot/root/data/stuff on /data/stuff (zfs, local, nfsv4acls) + + +# 볼륨 생성 +$ zfs create -V zroot/win_vm +$ zfs list zroot/win_vm +NAME USED AVAIL REFER MOUNTPOINT +zroot/win_vm 4.13G 17.9G 64K - +``` + +데이터셋 목록 보기 + +```bash +# 모든 데이터셋 목록 보기 +$ zfs list +NAME USED AVAIL REFER MOUNTPOINT +zroot 106G 30.8G 144K none +zroot/ROOT 18.5G 30.8G 144K none +zroot/ROOT/10.1 8K 30.8G 9.63G / +zroot/ROOT/default 18.5G 30.8G 11.2G / +zroot/backup 5.23G 30.8G 144K none +zroot/home 288K 30.8G 144K none +... + +# 특정 데이터셋 목록 보기 +$ zfs list zroot/home +NAME USED AVAIL REFER MOUNTPOINT +zroot/home 288K 30.8G 144K none + +# 스냅샷 목록 보기 +$ zfs list -t snapshot +zroot@daily-2015-10-15 0 - 144K - +zroot/ROOT@daily-2015-10-15 0 - 144K - +zroot/ROOT/default@daily-2015-10-15 0 - 24.2G - +zroot/tmp@daily-2015-10-15 124K - 708M - +zroot/usr@daily-2015-10-15 0 - 144K - +zroot/home@daily-2015-10-15 0 - 11.9G - +zroot/var@daily-2015-10-15 704K - 1.42G - +zroot/var/log@daily-2015-10-15 192K - 828K - +zroot/var/tmp@daily-2015-10-15 0 - 152K - +``` + +데이터셋 이름 변경 + +```bash +$ zfs rename zroot/root/home zroot/root/old_home +$ zfs rename zroot/root/new_home zroot/root/home +``` + +데이터셋 삭제 + +```bash +# 데이터셋에 스냅샷이 있으면 삭제할 수 없습니다 +$ zfs destroy zroot/root/home +``` + +데이터셋 속성 가져오기 / 설정하기 + +```bash +# 모든 속성 가져오기 +$ zfs get all zroot/usr/home +NAME PROPERTY VALUE SOURCE +zroot/home type filesystem - +zroot/home creation Mon Oct 20 14:44 2014 - +zroot/home used 11.9G - +zroot/home available 94.1G - +zroot/home referenced 11.9G - +zroot/home mounted yes - +... + +# 데이터셋에서 속성 가져오기 +$ zfs get compression zroot/usr/home +NAME PROPERTY VALUE SOURCE +zroot/home compression off default + +# 데이터셋에 속성 설정하기 +$ zfs set compression=lz4 zroot/lamb + +# 모든 데이터셋에서 속성 집합 가져오기 +$ zfs list -o name,quota,reservation +NAME QUOTA RESERV +zroot none none +zroot/ROOT none none +zroot/ROOT/default none none +zroot/tmp none none +zroot/usr none none +zroot/home none none +zroot/var none none +... +``` + +### 쓰기 로그 풀 + +ZFS 인텐트 로그(ZIL)는 동기적 쓰기 속도를 높이기 위해 설계된 쓰기 로그입니다. 이는 일반적으로 대용량 스토리지 풀보다 빠른 드라이브 또는 드라이브 파티션입니다. + +```bash +# 로그 풀 추가 +$ zpool add mypool/lamb log /dev/sdX + +# 설정 확인 +$ zpool status mypool/lamb +``` + +### 읽기 캐시 풀 + +레벨 2 적응형 교체 캐시(L2ARC)는 주 ARC(RAM 내 캐시)를 확장하며 읽기 캐싱에 사용됩니다. 이는 일반적으로 대용량 스토리지 풀보다 빠른 드라이브 또는 드라이브 파티션입니다. + +```bash +# 캐시 풀 추가 +$ zpool add mypool/lamb cache /dev/sdY + +# 설정 확인 +$ zpool status mypool/lamb +``` + +### 데이터 압축 + +데이터 압축은 약간의 추가 CPU 사용량을 대가로 데이터가 디스크에서 차지하는 공간의 양을 줄입니다. 활성화하면 디스크 I/O 양을 줄여 성능을 향상시킬 수 있습니다. 특히 디스크 대역폭보다 CPU 리소스가 더 많은 시스템에서 유용합니다. + +```bash +# 압축 옵션 가져오기 +$ zfs get -help +... +compression NO YES on | off | lzjb | gzip | gzip-[1-9] | zle | lz4 | zstd | zstd-[1-19] | zstd-fast | zstd-fast-[1-10,20,30,40,50,60,70,80,90,100,500,1000] +... + +# 압축 설정 +$ zfs set compression=on mypool/lamb + +# 설정 확인 +$ zpool get compression mypool/lamb +``` + +### 저장 데이터 암호화 + +암호화를 사용하면 추가 CPU 사이클을 비용으로 장치에서 데이터를 암호화할 수 있습니다. 이 속성은 데이터셋을 생성할 때만 설정할 수 있습니다. + +```bash +# 풀에 암호화 활성화 +$ zpool set feature@encryption=enabled black_hole + +# 프롬프트와 함께 암호화된 데이터셋 생성 +$ zfs create -o encryption=on -o keyformat=passphrase black_hole/enc + +# 설정 확인 +$ zfs get encryption black_hole/enc +``` + +데이터가 암호화되지 않는 시스템의 일부가 있다는 점에 유의해야 합니다. 자세한 내용은 아래 표를 참조하십시오. + +| 구성 요소 | 암호화 여부 | 참고 | +|----------------------|-------------------------------------------|------------------------------------------------------| +| 주 데이터 스토리지 | 예 | 데이터셋/볼륨의 데이터는 암호화됩니다. | +| ZFS 인텐트 로그 (ZIL) | 예 | 동기적 쓰기 요청은 암호화됩니다. | +| L2ARC (캐시) | 예 | 캐시된 데이터는 암호화된 형태로 저장됩니다. | +| RAM (ARC) | 아니요 | RAM에 있는 주 ARC의 데이터는 암호화되지 않습니다. | +| 스왑 영역 | 조건부 | ZFS 스왑 데이터셋이 암호화된 경우 암호화됩니다. | +| ZFS 메타데이터 | 예 | 암호화된 데이터셋의 메타데이터는 암호화됩니다. | +| 스냅샷 데이터 | 예 | 암호화된 데이터셋의 스냅샷도 암호화됩니다. | +| ZFS 전송/수신 | 조건부 | 데이터셋이 암호화되고 -w 플래그가 사용된 경우 전송/수신 중에 암호화됩니다. | + +### 스냅샷 + +ZFS 스냅샷은 zfs의 정말 중요한 기능 중 하나입니다. + +* 차지하는 공간은 파일 시스템과 스냅샷 간의 데이터 차이와 같습니다. +* 생성 시간은 단 몇 초에 불과합니다. +* 복구는 데이터를 쓰는 속도만큼 빠릅니다. +* 자동화하기 쉽습니다. + +작업: + +* 생성 +* 삭제 +* 이름 변경 +* 스냅샷 접근 +* 전송 / 수신 +* 복제 + +스냅샷 생성 + +```bash +# 단일 데이터셋의 스냅샷 생성 +zfs snapshot zroot/home/sarlalian@now + +# 데이터셋과 그 자식들의 스냅샷 생성 +$ zfs snapshot -r zroot/home@now +$ zfs list -t snapshot +NAME USED AV[6]AIL REFER MOUNTPOINT +zroot/home@now 0 - 26K - +zroot/home/sarlalian@now 0 - 259M - +zroot/home/alice@now 0 - 156M - +zroot/home/bob@now 0 - 156M - +... +``` + +스냅샷 제거 + +```bash +# 스냅샷 제거 방법 +$ zfs destroy zroot/home/sarlalian@now + +# 부모 데이터셋과 그 자식들의 스냅샷 삭제 +$ zfs destroy -r zroot/home/sarlalian@now +``` +[6] +스냅샷 이름 변경 + +```bash +# 스냅샷 이름 변경 +$ zfs rename zroot/home/sarlalian@now zroot/home/sarlalian@today +$ zfs rename zroot/home/sarlalian@now today + +$ zfs rename -r zroot/home@now @yesterday +``` + +스냅샷 접근 + +```bash +# 스냅샷 디렉터리로 이동(CD) +$ cd /home/.zfs/snapshot/ +``` + +전송 및 수신 + +```bash +# 스냅샷을 파일로 백업 +$ zfs send zroot/home/sarlalian@now | gzip > backup_file.gz + +# 스냅샷을 다른 데이터셋으로 전송 +$ zfs send zroot/home/sar[7]lalian@now | zfs recv backups/home/sarlalian + +# 스냅샷을 원격 호스트로 전송 +$ zfs send zroot/home/sarlalian@now | ssh root@backup_server 'zfs recv zroot/home/sarlalian' + +# 스냅샷과 함께 전체 데이터셋을 새 호스트로 전송 +$ zfs send -v -R zroot/home@now | ssh root@backup_server 'zfs recv zroot/home' +``` + +스냅샷 복제 + +```bash +# 스냅샷 복제 +$ zfs clone zroot/home/sarlalian@now zroot/home/sarlalian_new + +# 스냅샷에 더 이상 종속되지 않도록 클론을 승격 +$ zfs promote zroot/home/sarlalian_new +``` + +### 종합 예제 + +다음은 FreeBSD, Jails 및 ZFS를 활용하여 라이브 복제 슬레이브에서 MySQL 스테이징 데이터베이스의 깨끗한 복사본을 프로비저닝하는 것을 자동화하는 스크립트입니다. + +```bash +#!/bin/sh + +echo "==== 스테이징 데이터베이스 서버 중지 ====" +jail -r staging + +echo "==== 기존 스테이징 서버 및 스냅샷 정리 ====" +zfs destroy -r zroot/jails/staging +zfs destroy zroot/jails/slave@staging + +echo "==== 슬레이브 데이터베이스 정지 ====" +echo "FLUSH TABLES WITH READ LOCK;" | /usr/local/bin/mysql -u root -pmyrootpassword -h slave + +echo "==== 슬레이브 DB 파일 시스템을 zroot/jails/slave@staging으로 스냅샷 생성 ====" +zfs snapshot zroot/jails/slave@staging + +echo "==== 슬레이브 데이터베이스 서버 시작 ====" +jail -c slave + +echo "==== 슬레이브 스냅샷을 스테이징 서버로 복제 ====" +zfs clone zroot/jails/slave@staging zroot/jails/staging + +echo "==== 스테이징 MySQL 설정 설치 ====" +mv /jails/staging/usr/local/etc/my.cnf /jails/staging/usr/local/etc/my.cnf.slave +cp /jails/staging/usr/local/etc/my.cnf.staging /jails/staging/usr/local/etc/my.cnf + +echo "==== 스테이징 rc.conf 파일 설정 ====" +mv /jails/staging/etc/rc.conf.local /jails/staging/etc/rc.conf.slave +mv /jails/staging/etc/rc.conf.staging /jails/staging/etc/rc.conf.local + +echo "==== 스테이징 DB 서버 시작 ====" +jail -c staging + +echo "==== 스테이징 데이터베이스가 마스터에서 데이터를 가져오지 않도록 설정 ====" +echo "STOP SLAVE;" | /usr/local/bin/mysql -u root -pmyrootpassword -h staging +echo "RESET SLAVE;" | /usr/local/bin/mysql -u root -pmyrootpassword -h staging +``` + +### 추가 자료 + +* [BSDNow의 ZFS 속성 코스](http://www.bsdnow.tv/tutorials/zfs) +* [FreeBSD 핸드북 - ZFS](https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/zfs.html) +* [BSDNow의 ZFS 속성 코스](http://www.bsdnow.tv/tutorials/zfs) +* [Oracle 튜닝 가이드](http://www.oracle.com/technetwork/articles/servers-storage-admin/sto-recommended-zfs-settings-1951715.html) +* [OpenZFS 튜닝 가이드](http://open-zfs.org/wiki/Performance_tuning) +* [FreeBSD ZFS 튜닝 가이드](https://wiki.freebsd.org/ZFSTuningGuide) \ No newline at end of file diff --git a/ko/zig.md b/ko/zig.md new file mode 100644 index 0000000000..92f30586bb --- /dev/null +++ b/ko/zig.md @@ -0,0 +1,994 @@ +--- +name: Zig +filename: learnzig.zig +contributors: + - ["Philippe Pittoli", "https://karchnu.fr/"] +translators: + - ["Taeyoon Kim", "https://github.com/partrita"] +--- + +[Zig][ziglang]는 C 프로그래밍 언어를 대체하는 것을 목표로 합니다. + +**경고**: 이 문서는 컴퓨터 과학의 몇 가지 기본 개념(예: 포인터, 스택 및 힙 메모리 등)을 이해하고 있다고 가정합니다. C 언어에 대한 사전 지식이 권장됩니다. + +## 빠른 개요: C와 Zig 비교 + +- 구문은 대부분 동일하며, 일부 개선 사항이 있습니다 (모호함 감소). +- Zig는 네임스페이스를 도입했습니다. +- `try`와 `catch` 메커니미즘은 편리하고 효율적이며 선택 사항입니다. +- C의 대부분의 정의되지 않은 동작(UB)이 수정되었습니다. +- C에 비해 원시 포인터(raw pointer)는 사용하기에 더 안전하며 필요성이 적습니다. + - 타입 시스템은 단일 값에 대한 포인터, 여러 값에 대한 포인터 등을 구분합니다. + - 슬라이스(slice)가 선호되는데, 이는 포인터와 런타임에 알려진 크기를 가진 구조체로, 애초에 포인터의 대부분 사용 사례를 특징짓습니다. +- 일부 임의적인 언어 제한이 제거되었습니다. 예를 들어, 열거형, 구조체, 공용체는 함수를 가질 수 있습니다. +- SIMD 연산(벡터에 대한 기본 수학)에 간단하게 접근할 수 있습니다. +- Zig는 C의 저수준 기능과 컴파일러 확장을 통해 제공되는 기능을 모두 제공합니다. + 예: 압축 구조체(packed structure). +- 데이터 구조와 알고리즘을 포함한 광범위한 표준 라이브러리를 제공합니다. +- 어떠한 의존성 없이 기본적으로 크로스 컴파일 기능을 제공합니다. + 프로세스를 용이하게 하기 위해 다양한 libc가 제공됩니다. + 크로스 컴파일은 모든 운영 체제와 아키텍처 간에 작동합니다. + +## Zig 언어 + +```zig +//! 최상위 문서화 주석. + +/// 문서화 주석. + +// 간단한 주석. +``` + +### Hello world. + +```zig +// "std" 상수를 통해 접근할 수 있는 표준 라이브러리를 가져옵니다. +const std = @import("std"); + +// 이제 "info"는 "std.log.info" 함수를 참조합니다. +const info = std.log.info; + +// 일반적인 hello world. +// 구문: [pub] fn <함수-이름>(<인자들>) <반환-타입> { <본문> } +pub fn main() void { + // C 함수와 달리, Zig 함수는 고정된 수의 인자를 가집니다. + // C에서: "printf"는 임의의 수의 인자를 받습니다. + // Zig에서: std.log.info는 포맷과 출력할 요소 목록을 받습니다. + info("hello world", .{}); // .{}=는 빈 익명 튜플입니다. +} +``` + +### 불리언, 정수, 부동소수점. + +```zig +// 불리언. +// 불리언 연산에는 연산자보다 키워드가 선호됩니다. +print("{} +{} +{} +", .{ + true and false, + true or false, + !true, +}); + +// 정수. +const one_plus_one: i32 = 1 + 1; +print("1 + 1 = {} +", .{one_plus_one}); // 2 + +// 부동소수점. +const seven_div_three: f32 = 7.0 / 3.0; +print("7.0 / 3.0 = {} +", .{seven_div_three}); // 2.33333325e+00 + +// 정수는 임의의 값 길이를 가질 수 있습니다. +var myvar: u10 = 5; // 10비트 부호 없는 정수 +// 예를 들어 네트워크 패킷이나 복잡한 바이너리 형식을 읽는 데 유용합니다. + +// 숫자 표현은 C에 비해 크게 향상되었습니다. +const one_billion = 1_000_000_000; // 10진수. +const binary_mask = 0b1_1111_1111; // 2진수. 예: 네트워크 마스크. +const permissions = 0o7_5_5; // 8진수. 예: 유닉스 권한. +const big_address = 0xFF80_0000_0000_0000; // 16진수. 예: IPv6 주소. + + +// 오버플로 연산자: 컴파일러에게 오버플로가 괜찮을 때를 알려줍니다. +var i: u8 = 0; // "i"는 부호 없는 8비트 정수입니다. +i -= 1; // 런타임 오버플로 오류 (부호 없는 값은 항상 양수입니다) +i -%= 1; // 괜찮음 (wrapping 연산자), i == 255 + +// 포화 연산자: 값은 하한과 상한에 고정됩니다. +var i: u8 = 200; // "i"는 부호 없는 8비트 정수입니다 (값: 0에서 255까지) +i +| 100 == 255 // u8: 255보다 커지지 않습니다 +i -| 300 == 0 // 부호 없음, 0보다 작아지지 않습니다 +i *| 2 == 255 // u8: 255보다 커지지 않습니다 +i <<| 8 == 255 // u8: 255보다 커지지 않습니다 +``` + +### 배열. + +```zig +// 배열은 길이 속성(len)을 가진 잘 정의된 구조체입니다. + +// 정의되지 않은 내용(스택 쓰레기 값)을 가진 5바이트 배열. +var array1: [5]u8 = undefined; + +// 정의된 내용을 가진 5바이트 배열. +var array2 = [_]u8{ 1, 2, 3, 4, 5 }; +// [_]는 컴파일러가 컴파일 타임에 길이를 안다는 것을 의미합니다. + +// 정의된 내용(0)을 가진 1000바이트 배열. +var array3 = [_]u8{0} ** 1000; + +// 정의된 내용을 가진 또 다른 1000바이트 배열. +// 내용은 컴파일 타임에 호출되는 "foo" 함수에 의해 제공되며, +// 복잡한 초기화를 허용합니다. +var array4 = [_]u8{foo()} ** 1000; + +// 어떤 경우든, array.len은 배열의 길이를 제공합니다. +// array1.len과 array2.len은 5를, array3.len과 array4.len은 1000을 생성합니다. + + +// 배열 내용 수정 및 접근. + +// 10개의 32비트 정의되지 않은 정수 배열. +var some_integers: [10]i32 = undefined; + +some_integers[0] = 30; // 배열의 첫 번째 요소는 이제 30입니다. + +var x = some_integers[0]; // "x"는 이제 30과 같고, 타입이 추론됩니다. +var y = some_integers[1]; // 배열의 두 번째 요소는 정의되지 않았습니다. + // "y"는 스택 쓰레기 값을 가집니다 (런타임 오류 없음). + +// 10개의 32비트 정의되지 않은 정수 배열. +var some_integers: [10]i32 = undefined; + +var z = some_integers[20]; // 인덱스 > 배열 크기, 컴파일 오류. + +// 런타임에, 인덱스를 사용하여 "some_integers"의 요소를 반복합니다. +// 인덱스 i = 20일 때, 다음을 시도합니다: +try some_integers[i]; // 런타임 오류 'index out of bounds'. + // "try" 키워드는 인덱스로 배열에 접근할 때 필요합니다. + // 잠재적인 런타임 오류가 있기 때문입니다. + // 나중에 더 자세히 설명합니다. +``` + +### 다차원 배열. + +```zig +const mat4x4 = [4][4]f32{ + .{ 1, 0, 0, 0 }, + .{ 0, 1, 0, 1 }, + .{ 0, 0, 1, 0 }, + .{ 0, 0, 0, 1 }, +}; + +// 인덱스를 통해 2D 배열에 접근한 다음 내부 배열에 접근합니다. +try expect(mat4x4[1][1] == 1.0); + +// 여기서는 for 루프로 반복합니다. +for (mat4x4) |row, row_index| { + for (row) |cell, column_index| { + // ... + } +} +``` + +### 문자열. + +```zig +// 간단한 문자열 상수. +const greetings = "hello"; +// ... 이는 다음과 동일합니다: +const greetings: *const [5:0]u8 = "hello"; +// 말로 풀면: "greetings"는 상수 값이며, 5개 요소(8비트 부호 없는 정수)로 +// 이루어진 상수 배열에 대한 포인터이고, 끝에 추가적인 '0'이 있습니다. +// 추가적인 "0"은 "센티널 값(sentinel value)"이라고 불립니다. + +print("string: {s}\n", .{greetings}); + +// 이것은 C 문자열을 상당히 충실하게 나타냅니다. 하지만 Zig 문자열은 +// 구조체이므로, 크기를 계산하기 위해 "strlen"이 필요 없습니다. +// greetings.len == 5 +``` + +### 슬라이스. + +```zig +// 슬라이스는 포인터와 크기이며, 컴파일 타임에 크기를 알 수 없는 배열입니다. +// 슬라이스는 런타임에 경계 밖 검증을 합니다. + +const array = [_]u8{1,2,3,4,5}; // [_] = 컴파일 타임에 크기를 아는 배열. +const slice = array[0..array.len]; // "slice"는 전체 배열을 나타냅니다. + // slice[10]은 런타임 오류를 발생시킵니다. +``` + +### 포인터. + +```zig +// 값에 대한 포인터는 "&"로 생성할 수 있습니다. +const x: i32 = 1; +const pointer: *i32 = &x; // "pointer"는 i32 변수 "x"에 대한 포인터입니다. +print("1 = {}, {}\n", .{x, pointer}); + +// 포인터 값은 ".*"로 접근하고 수정합니다. +if (pointer.* == 1) { + print("x value == {} +", .{pointer.*}); +} + +// ".?"는 "orelse unreachable"의 단축 표현입니다. +const foo = pointer.?; // 가리키는 값을 가져오고, 그렇지 않으면 충돌합니다. +``` + +### 옵셔널 값 (?). + +```zig +// 옵셔널은 어떤 타입의 값이거나 null일 수 있는 값입니다. + +// 예: "optional_value"는 "null"이거나 부호 없는 32비트 정수일 수 있습니다. +var optional_value: ?u32 = null; // optional_value == null +optional_value = 42; // optional_value != null + +// "some_function"은 ?u32를 반환합니다. +var x = some_function(); +if (x) |value| { + // "some_function"이 값을 반환한 경우. + // 'value'로 무언가를 합니다. +} +``` + +### 오류. + +```zig +// Zig는 오류를 표현하는 통일된 방법을 제공합니다. + +// 오류는 오류 열거형에 정의됩니다. 예: +const Error = error { + WatchingAnyNetflixTVShow, + BeOnTwitter, +}; + +// 일반 열거형은 "enum" 키워드를 사용하여 동일한 방식으로 표현됩니다. +const SuccessStory = enum { + DoingSport, + ReadABook, +}; + + +// 오류 공용체 (!). +// "mylife" 값은 오류이거나 일반 값입니다. +var mylife: Error!SuccessStory = Error.BeOnTwitter; +// mylife는 오류입니다. 슬프네요. + +mylife = SuccessStory.ReadABook; +// 이제 mylife는 열거형입니다. + + +// Zig는 많은 미리 정의된 오류와 함께 제공됩니다. 예: +const value: anyerror!u32 = error.Broken; + + +// 오류 처리. + +// 몇 가지 오류 예제. +const Error = error { + UnExpected, + Authentication, +}; + +// "some_function"은 "Error" 또는 정수를 반환할 수 있습니다. +fn some_function() Error!u8 { + return Error.UnExpected; // 오류를 반환합니다. +} + +// 오류는 중간 변수 없이 "catch"될 수 있습니다. +var value = some_function() catch |err| switch(err) { + Error.UnExpected => return err, // 오류를 반환합니다. + Error.Authentication => unreachable, // 예상되지 않음. 프로그램을 충돌시킵니다. + else => unreachable, +}; + +// 오류는 이름을 지정하지 않고 "catch"될 수 있습니다. +const unwrapped = some_function() catch 1234; // "unwrapped" = 1234 + +// "try"는 "catch |err| return err"에 대한 매우 편리한 단축 표현입니다. +var value = try some_function(); +// "some_function"이 실패하면, 현재 함수는 중지하고 오류를 반환합니다. +// "value"는 유효한 값만 가질 수 있으며, 오류는 이미 "try"로 처리되었습니다. +``` + +### 제어 흐름. + +```zig +// 조건 분기. + +if (condition) { + ... +} +else { + ... +} + +// 삼항 연산자. +var value = if (condition) x else y; + +// "if (x) x else 0"의 단축 표현 +var value = x orelse 0; + +// "a"가 값을 포함할 수 있는 옵셔널인 경우. +if (a) |value| { + print("value: {} +", .{value}); +} +else { + print("'a' is null\n", .{}); +} + +// 값에 대한 포인터를 가져옵니다 (존재하는 경우). +if (a) |*value| { value.* += 1; } + + +// 루프. + +// 구문 예제: +// while (condition) statement +// while (condition) : (end-of-iteration-statement) statement +// +// for (iterable) statement +// for (iterable) |capture| statement +// for (iterable) statement else statement + +// 참고: 루프는 배열이나 슬라이스에 대해 동일하게 작동합니다. + +// 간단한 "while" 루프. +while (i < 10) { i += 1; } + +// "continue expression"이 있는 while 루프 +// (루프의 마지막 표현식으로 실행되는 표현식). +while (i < 10) : (i += 1) { ... } +// 더 복잡한 continue expression(코드 블록)을 사용한 동일한 예. +while (i * j < 2000) : ({ i *= 2; j *= 3; }) { ... } + +// 슬라이스의 일부를 반복하려면, 다시 슬라이스합니다. +for (items[0..1]) |value| { sum += value; } + +// 배열(또는 슬라이스)의 모든 항목을 반복합니다. +for (items) |value| { sum += value; } + +// 복사본 대신 값에 대한 포인터를 가져와 반복합니다. +for (items) |*value| { value.* += 1; } + +// 인덱스와 함께 반복합니다. +for (items) |value, i| { print("val[{}] = {} +", .{i, value}); } + +// 포인터와 인덱스와 함께 반복합니다. +for (items) |*value, i| { print("val[{}] = {} +", .{i, value}); value.* += 1; } + + +// break와 continue가 지원됩니다. +for (items) |value| { + if (value == 0) { continue; } + if (value >= 10) { break; } + // ... +} + +// for 루프는 표현식으로도 사용될 수 있습니다. +// while 루프와 유사하게, for 루프에서 break하면, +// else 분기는 평가되지 않습니다. +var sum: i32 = 0; +// "for" 루프는 값을 제공해야 하며, 이는 "else" 값이 됩니다. +const result = for (items) |value| { + if (value != null) { + sum += value.?; // "result"는 마지막 "sum" 값이 됩니다. + } +} else 0; // 마지막 값. +``` + +### 레이블. + +```zig +// 레이블은 명령어, 코드 내 위치에 이름을 지정하는 방법입니다. +// 레이블은 중첩 루프에서 "continue" 또는 "break"하는 데 사용될 수 있습니다. +outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| { + for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { + count += 1; + continue :outer; // 첫 번째 루프에 대해 "continue"합니다. + } +} // count = 8 +outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| { + for ([_]i32{ 1, 2, 3, 4, 5 }) |_| { + count += 1; + break :outer; // 첫 번째 루프에 대해 "break"합니다. + } +} // count = 1 + + +// 레이블은 블록에서 값을 반환하는 데에도 사용될 수 있습니다. +var y: i32 = 5; +const x = blk: { + y += 1; + break :blk y; // 이제 "x"는 6과 같습니다. +}; +// "for else" 표현식과 같은 경우에 관련이 있습니다 (다음에 설명). + +// for 루프는 표현식으로 사용될 수 있습니다. +// for 루프에서 break하면, else 분기는 평가되지 않습니다. +// 경고: 직관에 반합니다. +// "for" 루프가 실행된 다음 "else" 블록이 실행됩니다. +// "else" 키워드 다음에는 "result"에 줄 값이 와야 합니다. +// 다른 형태는 나중에 참조하십시오. +var sum: u8 = 0; +const result = for (items) |value| { + sum += value; +} else 8; // result = 8 + +// 이 경우에도 "else" 키워드 다음에는 값이 옵니다. +// 그러나 구문이 다릅니다: 레이블이 지정되어 있습니다. +// 값 대신, 레이블과 코드 블록이 있으며, 이는 +// 값을 반환하기 전에 작업을 수행할 수 있게 합니다 ("break" 호출 참조). +const result = for (items) |value| { // 첫째: 루프. + sum += value; +} else blk: { // 둘째: "else" 블록. + std.log.info("executed AFTER the loop!", .{}); + break :blk sum; // "sum" 값이 레이블 "blk"를 대체합니다. +}; +``` + +### Switch. + +```zig +// C의 switch와 같지만, 약간 더 발전했습니다. +// 구문: +// switch (value) { +// pattern => expression, +// pattern => expression, +// else => expression +// }; + +// 간단한 값만 확인하는 switch. +var x = switch(value) { + Error.UnExpected => return err, + Error.Authentication => unreachable, + else => unreachable, +}; + +// 값의 범위를 허용하는 약간 더 발전된 switch: +const foo: i32 = 0; +const bar = switch (foo) { + 0 => "zero", + 1...std.math.maxInt(i32) => "positive", + else => "negative", +}; +``` + +### 구조체. + +```zig +// 단일 값을 포함하는 구조체. +const Full = struct { + number: u16, +}; + +// 메모리 내 레이아웃이 보장되는 압축 구조체. +const Divided = packed struct { + half1: u8, + quarter3: u4, + quarter4: u4, +}; + +// Point는 두 개의 u32, "x"와 "y"를 포함하는 구조체를 나타내는 상수입니다. +// "x"는 기본값을 가지며, 이는 C에서는 불가능했습니다. +const Point = struct { + x: u32 = 1, // 기본값 + y: u32, +}; + +// 변수 "p"는 x = 1 (기본값)이고 y = 2인 새로운 Point입니다. +var p = Point{ .y = 2 }; + +// 필드는 점 표기법으로 평소와 같이 접근합니다: variable.field. +print("p.x: {} +", .{p.x}); // 1 +print("p.y: {} +", .{p.y}); // 2 + + +// 구조체는 공개 상수와 함수를 포함할 수도 있습니다. +const Point = struct { + pub const some_constant = 30; + + x: u32, + y: u32, + + // 이 "init" 함수는 Point를 생성하고 반환합니다. + pub fn init() Point { + return Point{ .x = 0, .y = 0 }; + } +}; + + +// 구조체 공개 상수에 접근하는 방법. +// 값은 구조체의 "인스턴스"에서 접근하는 것이 아니라, +// 구조체 정의를 나타내는 상수(Point)에서 접근합니다. +print("constant: {} +", .{Point.some_constant}); + +// "init" 함수를 갖는 것은 표준 라이브러리에서 상당히 관용적입니다. +// 나중에 더 자세히 설명합니다. +var p = Point.init(); +print("p.x: {} +", .{p.x}); // p.x = 0 +print("p.y: {} +", .{p.y}); // p.y = 0 + + +// 구조체는 종종 객체 지향 프로그래밍과 유사하게 +// 상태를 수정하는 함수를 가집니다. +const Point = struct { + const Self = @This(); // 자신의 타입(나중에 "Point"라고 불림)을 참조합니다. + + x: u32, + y: u32, + + // 시그니처를 보세요. 첫 번째 인자는 *Self 타입입니다: "self"는 + // 구조체의 인스턴스에 대한 포인터입니다. + // 이것은 OOP에서와 같은 "점" 표기법을 허용합니다, 예: "instance.set(x,y)". + // 다음 예제를 참조하십시오. + pub fn set(self: *Self, x: u32, y: u32) void { + self.x = x; + self.y = y; + } + + // 다시, 시그니처를 보세요. 첫 번째 인자는 Self 타입입니다 (*Self가 아님), + // 이것은 포인터가 아닙니다. 이 경우, "self"는 구조체의 인스턴스를 + // 참조하지만, 수정될 수 없습니다. + pub fn getx(self: Self) u32 { + return self.x; + } + + // PS: 이전 두 함수는 다소 쓸모없을 수 있습니다. + // 속성은 직접 변경할 수 있으므로 접근자 함수가 필요 없습니다. + // 이것은 단지 예시였습니다. +}; + +// 이전 구조체를 사용해 봅시다. +var p = Point{ .x = 0, .y = 0 }; // "p" 변수는 Point입니다. + +p.set(10, 30); // "p"의 x와 y 속성은 "set" 함수를 통해 수정됩니다. +print("p.x: {} +", .{p.x}); // 10 +print("p.y: {} +", .{p.y}); // 30 + +// C에서: +// 1. 우리는 point_set(p, 10, 30)과 같이 작성했을 것입니다. +// 2. 모든 함수가 동일한 네임스페이스에 있기 때문에, 다른 구조체에 대해 +// 다른 이름의 함수를 만드는 것이 매우 번거로웠을 것입니다. +// 많은 긴 이름, 읽기 고통스러움. +// +// Zig에서, 구조체는 자신의 함수에 대한 네임스페이스를 제공합니다. +// 다른 구조체는 함수에 대해 동일한 이름을 가질 수 있으며, +// 이는 명확성을 가져옵니다. +``` + +### 튜플. + +```zig +// 튜플은 잠재적으로 다른 타입의 요소 목록입니다. + +const foo = .{ "hello", true, 42 }; +// foo.len == 3 +``` + +### 열거형. + +```zig +const Type = enum { ok, not_ok }; + +const CardinalDirections = enum { North, South, East, West }; +const direction: CardinalDirections = .North; +const x = switch (direction) { + // CardinalDirections.North의 단축 표현 + .North => true, + else => false +}; + +// Switch 문은 완전성을 요구합니다. +// 경고: 컴파일되지 않습니다. East와 West가 빠졌습니다. +const x = switch (direction) { + .North => true, + .South => true, +}; + +// 이것은 모든 가능한 값을 철저히 나열하므로 오류 없이 컴파일됩니다. +const x = switch (direction) { + .North => true, + .South => true, + .East, // 그 값은 다음 패턴과 동일합니다: false. + .West => false, +}; + + +// 열거형은 구조체와 같습니다: 함수를 가질 수 있습니다. +``` + +### 공용체. + +```zig +const Bar = union { + boolean: bool, + int: i16, + float: f32, +}; + +// 두 구문은 동일합니다. +const foo = Bar{ .int = 42 }; +const foo: Bar = .{ .int = 42 }; + +// 공용체는 열거형 및 구조체와 마찬가지로 함수를 가질 수 있습니다. +``` + +### 태그된 공용체. + +```zig +// 공용체는 enum 태그 타입으로 선언될 수 있으며, switch 표현식에서 +// 사용될 수 있습니다. + +const MaybeEnum = enum { + success, + failure, +}; + +const Maybe = union(MaybeEnum) { + success: u8, + failure: []const u8, +}; + +// 첫 번째 값: 성공! +const yay = Maybe{ .success = 42 }; +switch (yay) { + .success => |value| std.log.info("success: {}", .{value}), + .failure => |err_msg| std.log.info("failure: {}", .{err_msg}), +} + +// 두 번째 값: 실패! :( +const nay = Maybe{ .failure = "I was too lazy" }; +switch (nay) { + .success => |value| std.log.info("success: {}", .{value}), + .failure => |err_msg| std.log.info("failure: {}", .{err_msg}), +} +``` + +### Defer와 errdefer. + +```zig +// 어떤 동작(단일 명령어 또는 코드 블록)이 스코프(함수, 코드 블록)의 +// 끝 이전에 실행되도록 보장합니다. +// 오류 발생 시에도 해당 동작은 실행됩니다. +// 메모리 할당 및 일반적인 리소스 관리에 유용합니다. + +pub fn main() void { + // 함수 끝에서 실행되어야 합니다. + defer print("third!\n", .{}); + + { + // 스코프의 마지막 요소: 즉시 실행됩니다. + defer print("first!\n", .{}); + } + + print("second!\n", .{}); +} + +fn hello_world() void { + defer print("end of function\n", .{}); // "hello world!" 이후 + + print("hello world!\n", .{}); +} + +// errdefer는 오류 발생 시에만 명령어(또는 코드 블록)를 실행합니다. +fn second_hello_world() !void { + errdefer print("2. something went wrong!\n", .{}); // "foo"가 실패하면. + defer print("1. second hello world\n", .{}); // "foo" 이후에 실행됨 + + try foo(); +} +// Defer 문은 스택처럼 쌓이는 것으로 볼 수 있습니다: 첫 번째 것이 마지막에 실행됩니다. +``` + +### 메모리 할당자. +메모리는 표준 라이브러리에서 직접 관리되지 않고, 메모리에 대한 작업이 필요할 때마다 "할당자"에게 요청됩니다. +따라서 표준 라이브러리는 개발자가 모든 메모리 작업을 처리하는 "할당자"라는 구조체를 통해 필요에 따라 메모리를 처리하도록 합니다. + +**참고**: 할당자의 선택은 이 문서의 범위를 벗어납니다. +그것에 대해 책 한 권을 쓸 수도 있습니다. +그러나 여기에 몇 가지 예가 있습니다. 어떤 것을 기대할 수 있는지에 대한 아이디어를 얻기 위해: + +- `page_allocator`. + 메모리를 요청할 때마다 메모리 페이지 전체를 할당합니다. + 매우 간단하고, 매우 멍청하며, 매우 낭비적입니다. +- `GeneralPurposeAllocator`. + 먼저 일부 메모리를 얻고 메모리 버킷을 관리하여 + 할당 횟수를 줄입니다. + 조금 복잡합니다. 다른 할당자와 결합할 수 있습니다. + 누수를 감지하고 그것들을 찾는 데 유용한 정보를 제공할 수 있습니다. +- `FixedBufferAllocator`. + 메모리를 얻기 위해 고정 버퍼를 사용하고, 커널에 메모리를 요청하지 않습니다. + 매우 간단하고, 제한적이며, 낭비적이지만(할당 해제 불가), 매우 빠릅니다. +- `ArenaAllocator`. + 할당된 모든 메모리를 한 번에 해제할 수 있습니다. + 다른 할당자와 조합하여 사용합니다. + 누수를 피하는 매우 간단한 방법입니다. + +첫 번째 예제. + +```zig +// "!void"는 함수가 오류를 제외하고는 어떤 값도 반환하지 않음을 의미합니다. +// 이 경우 메모리 할당을 시도하며, 실패할 수 있습니다. +fn foo() !void { + // 이 예제에서는 페이지 할당자를 사용합니다. + var allocator = std.heap.page_allocator; + + // "list"는 8비트 부호 없는 정수의 ArrayList입니다. + // ArrayList는 메모리에서 연속적이고 확장 가능한 요소 목록입니다. + var list = try ArrayList(u8).initAllocated(allocator); + defer list.deinit(); // 스코프 끝에서 메모리를 해제합니다. 누수될 수 없습니다. + // "defer"는 함수의 복잡성(루프, 조건 등)에 관계없이 + // 할당 직후에 메모리 해제를 표현할 수 있게 합니다. + + list.add(5); // 제공된 할당자로 여기서 일부 메모리가 할당됩니다. + + for (list.items) |item| { + std.debug.print("item: {} +", .{item}); + } +} +``` + +### 메모리 할당과 오류 관리 및 defer의 결합. + +```zig +fn some_memory_allocation_example() !void { + // 메모리 할당은 실패할 수 있으므로, 메모리 할당을 "try"하고 + // 오류가 발생하면 현재 함수가 그것을 반환합니다. + var buf = try page_allocator.alloc(u8, 10); + // 할당 직후에 메모리 해제를 defer합니다. + // 오류가 발생하더라도 일어날 것입니다. + defer page_allocator.free(buf); + + // 두 번째 할당. + // 실패 시, 첫 번째 할당은 올바르게 해제됩니다. + var buf2 = try page_allocator.alloc(u8, 10); + defer page_allocator.free(buf2); + + // 실패 시, 이전 두 할당 모두 올바르게 할당 해제됩니다. + try foo(); + try bar(); + + // ... +} +``` + +### 메모리 할당자: 표준 라이브러리 맛보기. + +```zig +// 할당자: 알아야 할 4가지 주요 함수 +// single_value = create (type) +// destroy (single_value) +// slice = alloc (type, size) +// free (slice) + +// 페이지 할당자 +fn page_allocator_fn() !void { + var slice = try std.heap.page_allocator.alloc(u8, 3); + defer std.heap.page_allocator.free(slice); + + // playing_with_a_slice(slice); +} + +// GeneralPurposeAllocator +fn general_purpose_allocator_fn() !void { + // GeneralPurposeAllocator는 구성되어야 합니다. + // 이 경우, 메모리 누수를 추적하고 싶습니다. + const config = .{.safety = true}; + var gpa = std.heap.GeneralPurposeAllocator(config){}; + defer _ = gpa.deinit(); + + const allocator = gpa.allocator(); + + var slice = try allocator.alloc(u8, 3); + defer allocator.free(slice); + + // playing_with_a_slice(slice); +} + +// FixedBufferAllocator +fn fixed_buffer_allocator_fn() !void { + var buffer = [_]u8{0} ** 1000; // 1000개의 u8 배열, 모두 0으로 초기화됨. + var fba = std.heap.FixedBufferAllocator.init(buffer[0..]); + // 참고: buffer[0..]는 배열에서 슬라이스를 만드는 방법입니다. + // 함수가 배열이 아닌 슬라이스를 받기 때문에, 이것은 + // 타입 시스템을 만족시킵니다. + + var allocator = fba.allocator(); + + var slice = try allocator.alloc(u8, 3); + // "free"가 필요 없습니다, 고정 버퍼 할당자로는 메모리를 해제할 수 없습니다. + // defer allocator.free(slice); + + // playing_with_a_slice(slice); +} + +// ArenaAllocator +fn arena_allocator_fn() !void { + // 알림: 아레나는 메모리를 할당하지 않고, 내부 할당자를 사용합니다. + // 이 경우, 아레나 할당자를 페이지 할당자와 결합합니다. + var arena = std.heap.arena_allocator.init(std.heap.page_allocator); + defer arena.deinit(); // 함수 끝 = 모든 할당이 해제됩니다. + + var allocator = arena.allocator(); + + const slice = try allocator.alloc(u8, 3); + // "free"가 필요 없습니다, 메모리는 어쨌든 해제될 것입니다. + + // playing_with_a_slice(slice); +} + + +// 범용 및 아레나 할당자 결합. 둘 다 매우 유용하며, +// 그들의 조합은 모든 사람의 즐겨찾는 요리책에 있어야 합니다. +fn gpa_arena_allocator_fn() !void { + const config = .{.safety = true}; + var gpa = std.heap.GeneralPurposeAllocator(config){}; + defer _ = gpa.deinit(); + + const gpa_allocator = gpa.allocator(); + + var arena = arena_allocator.init(gpa_allocator); + defer arena.deinit(); + + const allocator = arena.allocator(); + + var slice = try allocator.alloc(u8, 3); + defer allocator.free(slice); + + // playing_with_a_slice(slice); +} +``` + +### Comptime. + +```zig +// Comptime은 전처리기를 피하는 방법입니다. +// 아이디어는 간단합니다: 컴파일 시에 코드를 실행합니다. + +inline fn max(comptime T: type, a: T, b: T) T { + return if (a > b) a else b; +} + +var res = max(u64, 1, 2); +var res = max(f32, 10.50, 32.19); + + +// Comptime: 제네릭 구조체 생성. + +fn List(comptime T: type) type { + return struct { + items: []T, + + fn init() ... { ... } + fn deinit() ... { ... } + fn do() ... { ... } + }; +} + +const MyList = List(u8); + + +// 사용 +var list = MyList{ + .items = ... // 메모리 할당 +}; + +list.items[0] = 10; +``` + +### 조건부 컴파일. + +```zig +const available_os = enum { OpenBSD, Linux }; +const myos = available_os.OpenBSD; + + +// 다음 switch는 상수 값을 기반으로 합니다. +// 이것은 유일하게 가능한 결과가 컴파일 타임에 알려져 있음을 의미합니다. +// 따라서 나머지 가능성을 빌드할 필요가 없습니다. +// C의 "#ifdef"와 유사하지만, 전처리기가 필요 없습니다. +const string = switch (myos) { + .OpenBSD => "OpenBSD is awesome!", + .Linux => "Linux rocks!", +}; + +// 이 경우에도 작동합니다. +const myprint = switch(myos) { + .OpenBSD => std.debug.print, + .Linux => std.log.info, +} +``` + +### 함수 테스트하기. + +```zig +const std = @import("std"); +const expect = std.testing.expect; + +// 테스트할 함수. +pub fn some_function() bool { + return true; +} + +// 이 "test" 블록은 "zig test"로 실행할 수 있습니다. +// 컴파일 타임에 함수를 테스트합니다. +test "returns true" { + expect(false == some_function()); +} +``` + +### 컴파일러 내장 기능. + +컴파일러에는 "@"로 시작하는 "내장 기능(built-ins)"이라는 특수 함수가 있습니다. +백 개가 넘는 내장 기능이 있으며, 매우 저수준의 작업을 허용합니다: + +- 컴파일 타임 오류, 로깅, 검증 +- 안전하지 않은 방식까지 포함한 타입 강제 변환 및 변환 +- 정렬 관리 +- 메모리 트릭 (예: 구조체에서 필드의 바이트 오프셋 가져오기) +- 컴파일 타임에 함수 호출 +- 실행 파일에 파일 포함 (@embedFile) +- 프레임 조작 (예: 비동기 함수용) +- 등등. + +예: 열거형은 정수가 아니므로, 내장 기능으로 변환해야 합니다. + +```zig +const Value = enum { zero, stuff, blah }; +if (@enumToInt(Value.zero) == 0) { ... } +if (@enumToInt(Value.stuff) == 1) { ... } +if (@enumToInt(Value.blah) == 2) { ... } +``` + +### Zig 언어의 몇 가지 "제 발등 찍지 않기" 조치. + +- 네임스페이스: 이름 충돌을 쉽게 피할 수 있습니다. + 실제로, 이는 다른 구조체(데이터 타입) 간의 통일된 API를 의미합니다. +- 열거형은 정수가 아닙니다. 열거형을 정수와 비교하려면 변환이 필요합니다. +- 명시적 캐스트, 강제 변환은 존재하지만 제한적입니다. + 타입은 C보다 약간 더 강제됩니다, 맛보기: + 포인터는 정수가 아니며, 명시적 변환이 필요합니다. + 실수로 정밀도를 잃지 않으며, 암시적 강제 변환은 정밀도를 잃을 수 없는 경우에만 허용됩니다. + 공용체는 재해석될 수 없습니다 (정수와 부동소수점이 있는 공용체에서, 실수로 다른 값으로 취할 수 없음). + 등등. +- C의 대부분의 정의되지 않은 동작(UB)을 제거하고, 컴파일러가 하나를 만나면 중지합니다. +- 포인터보다 슬라이스와 배열 구조체가 선호됩니다. + 컴파일러에 의해 강제되는 타입은 포인터 조작보다 오류가 발생하기 쉽습니다. +- 숫자 오버플로는 wrapping 연산자를 사용하여 명시적으로 허용되지 않는 한 오류를 생성합니다. +- `try`와 `catch` 메커니즘. + 편리하고, 간단하게 구현되며(간단한 오류 열거형), 공간이나 계산 시간을 거의 차지하지 않습니다. +- 사용되지 않는 변수는 컴파일러에 의해 오류로 간주됩니다. +- 가리키는 것을 나타내기 위해 많은 포인터 타입이 존재합니다. + 예: 이것이 단일 값인가 배열인가, 길이가 알려져 있는가 등. +- 구조체는 속성에 대한 값이 필요하며, 정의되지 않은 값(스택 쓰레기)을 주는 것은 여전히 가능하지만, 적어도 명시적으로 정의되지 않았습니다. + +## 추가 자료 + +시작으로, 몇 가지 개념이 [zig.guide][zigguide]에 소개되어 있습니다. + +[공식 웹사이트][zigdoc]는 언어의 참조 문서를 제공합니다. 표준 라이브러리는 [자체 문서][zigstd]를 가지고 있습니다. + +[ziglang]: https://ziglang.org +[zigguide]: https://zig.guide/ +[zigdoc]: https://ziglang.org/documentation/ +[zigstd]: https://ziglang.org/documentation/master/std/ \ No newline at end of file