diff --git a/gen-translation.go b/gen-translation.go new file mode 100644 index 0000000..16624e8 --- /dev/null +++ b/gen-translation.go @@ -0,0 +1,168 @@ +//go:build ignore + +// gen-translation generates skeleton .md translation files for a given language. +// +// Usage: +// +// go run gen-translation.go [tutorial-dir...] +// +// Examples: +// +// go run gen-translation.go zh # generate for all tutorials +// go run gen-translation.go zh 101-Hello-world # generate for specific tutorial +// go run gen-translation.go ja 101-Hello-world 102-Values +// +// The generated .md files contain the original English doc segments as placeholders, +// separated by "---". Translators replace the English text with the target language. +// If a .md file already exists, it will NOT be overwritten. +package main + +import ( + "fmt" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" +) + +const chNumLen = 3 + +func main() { + if len(os.Args) < 2 { + fmt.Fprintln(os.Stderr, "Usage: go run gen-translation.go [tutorial-dir...]") + os.Exit(1) + } + lang := os.Args[1] + if !regexp.MustCompile(`^[a-z]{2,10}$`).MatchString(lang) { + fmt.Fprintln(os.Stderr, "Error: lang must be a simple language code (e.g. zh, ja, ko)") + os.Exit(1) + } + + var dirs []string + if len(os.Args) > 2 { + dirs = os.Args[2:] + } else { + // Find all tutorial directories + fis, err := os.ReadDir(".") + if err != nil { + fmt.Fprintln(os.Stderr, "Error reading current directory:", err) + os.Exit(1) + } + for _, fi := range fis { + if fi.IsDir() { + name := fi.Name() + if len(name) > (chNumLen+1) && name[chNumLen] == '-' { + if _, e := strconv.Atoi(name[:chNumLen]); e == nil { + // Skip chapter headings (e.g. 100-, 200-) + if !strings.HasSuffix(name[:chNumLen], "00") { + dirs = append(dirs, name) + } + } + } + } + } + } + + total := 0 + skipped := 0 + for _, dir := range dirs { + fis, err := os.ReadDir(dir) + if err != nil { + fmt.Fprintf(os.Stderr, "Warning: cannot read %s: %v\n", dir, err) + continue + } + langDir := filepath.Join("locales", lang, dir) + dirCreated := false + for _, fi := range fis { + fname := fi.Name() + ext := filepath.Ext(fname) + if ext != ".gop" && ext != ".xgo" { + continue + } + + mdName := strings.TrimSuffix(fname, ext) + ".md" + mdPath := filepath.Join("locales", lang, dir, mdName) + + // Skip if already exists + if _, err := os.Stat(mdPath); err == nil { + skipped++ + continue + } else if !os.IsNotExist(err) { + fmt.Fprintf(os.Stderr, "Warning: cannot stat %s: %v\n", mdPath, err) + continue + } + + // Parse .gop to extract doc segments + content, err := os.ReadFile(filepath.Join(dir, fname)) + if err != nil { + fmt.Fprintf(os.Stderr, "Warning: cannot read %s: %v\n", fname, err) + continue + } + + docs := extractDocSegments(string(content)) + if len(docs) == 0 { + continue + } + + // Create lang directory once per tutorial dir + if !dirCreated { + if err := os.MkdirAll(langDir, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error creating %s: %v\n", langDir, err) + break + } + dirCreated = true + } + + // Write skeleton .md + skeleton := strings.Join(docs, "\n\n---\n\n") + if err := os.WriteFile(mdPath, []byte(skeleton+"\n"), 0644); err != nil { + fmt.Fprintf(os.Stderr, "Error writing %s: %v\n", mdPath, err) + continue + } + + fmt.Printf("Created: %s (%d segments)\n", mdPath, len(docs)) + total++ + } + } + fmt.Printf("\nDone: %d files created, %d skipped (already exist)\n", total, skipped) +} + +// extractDocSegments parses a .gop file and returns the text of each doc segment. +func extractDocSegments(content string) []string { + lines := strings.Split(content, "\n") + var segments []string + var current []string + inDoc := false + + for _, line := range lines { + trimmed := strings.TrimSpace(line) + isDoc := false + var docText string + + if strings.HasPrefix(trimmed, "//") { + isDoc = true + docText = strings.TrimPrefix(trimmed[2:], " ") + } else if strings.HasPrefix(trimmed, "#") && !strings.HasPrefix(trimmed, "#!") { + isDoc = true + docText = "## " + strings.TrimPrefix(strings.TrimPrefix(trimmed, "#"), " ") + } + + if isDoc { + current = append(current, docText) + inDoc = true + } else { + if inDoc && len(current) > 0 { + segments = append(segments, strings.Join(current, "\n")) + current = nil + } + inDoc = false + } + } + // Flush last segment + if len(current) > 0 { + segments = append(segments, strings.Join(current, "\n")) + } + + return segments +} diff --git a/locales/en.json b/locales/en.json new file mode 100644 index 0000000..2d1f28e --- /dev/null +++ b/locales/en.json @@ -0,0 +1,13 @@ +{ + "site_title": "XGo by Tutorials", + "breadcrumb_tutorials": "Tutorials", + "index_heading": "Tutorials", + "index_desc_1": "XGo is an open source programming language aimed to enable everyone to become a builder of the world.", + "index_desc_2": "XGo by Tutorials is a hands-on introduction to XGo using annotated example programs. Check out the first example or browse the full list below.", + "no_content_before": "No content yet, you can help us build it ", + "no_content_link": "here", + "no_content_after": ".", + "next_example": "Next example:", + "lang_switcher_label": "Switch language", + "titles": {} +} diff --git a/locales/ja.json b/locales/ja.json new file mode 100644 index 0000000..2ffc939 --- /dev/null +++ b/locales/ja.json @@ -0,0 +1,60 @@ +{ + "site_title": "XGo チュートリアル", + "breadcrumb_tutorials": "チュートリアル", + "index_heading": "チュートリアル", + "index_desc_1": "XGo は、すべての人が世界の構築者になれることを目指したオープンソースプログラミング言語です。", + "index_desc_2": "XGo チュートリアルは、注釈付きのサンプルプログラムを通じて XGo を学ぶ実践的なチュートリアルです。最初の例をご覧になるか、以下の一覧からお選びください。", + "no_content_before": "まだコンテンツがありません。", + "no_content_link": "こちら", + "no_content_after": "で作成にご協力ください。", + "next_example": "次の例:", + "lang_switcher_label": "言語を切り替える", + "titles": { + "Sequential programming": "Sequential Programming 順次プログラミング", + "Structured programming": "Structured Programming 構造化プログラミング", + "Hello world": "Hello World", + "Values": "Values 値", + "Constants": "Constants 定数", + "Variables": "Variables 変数", + "Assignments": "Assignments 代入", + "Types": "Types 型", + "Integers": "Integers 整数", + "Floating-Point Numbers": "Floating-Point Numbers 浮動小数点数", + "Complex Numbers": "Complex Numbers 複素数", + "Booleans": "Booleans ブーリアン", + "Strings": "Strings 文字列", + "Rational Numbers": "Rational Numbers 有理数", + "If/Else": "If/Else 条件分岐", + "Switch": "Switch 分岐", + "For": "For ループ", + "Arrays": "Arrays 配列", + "Slices": "Slices スライス", + "Maps": "Maps マップ", + "Structs": "Structs 構造体", + "Pointers": "Pointers ポインタ", + "For Each": "For Each 各要素の走査", + "For Range": "For Range range 節", + "List Comprehension": "List Comprehension リスト内包表記", + "Functions": "Functions 関数", + "Multiple Return Values": "Multiple Return Values 複数戻り値", + "Errors": "Errors エラー処理", + "Function Values": "Function Values 関数値", + "Closures": "Closures クロージャ", + "Lambda expressions": "Lambda Expressions ラムダ式", + "Recursion": "Recursion 再帰", + "Variadic Parameters": "Variadic Parameters 可変長引数", + "Defer": "Defer 遅延実行", + "Exceptions": "Exceptions 例外処理", + "Methods": "Methods メソッド", + "Methods with a Pointer Receiver": "Methods with a Pointer Receiver ポインタレシーバのメソッド", + "Composing Types by Struct Embedding": "Composing Types by Struct Embedding 構造体埋め込みによる型合成", + "Method Values and Expressions": "Method Values and Expressions メソッド値とメソッド式", + "Encapsulation": "Encapsulation カプセル化", + "Interfaces": "Interfaces インターフェース", + "Interface Satisfaction": "Interface Satisfaction インターフェース実装", + "Interface Values": "Interface Values インターフェース値", + "The error Interface": "The error Interface エラーインターフェース", + "Type Assertions": "Type Assertions 型アサーション", + "Type Switches": "Type Switches 型スイッチ" + } +} diff --git a/locales/ja/101-Hello-world/hello-11.md b/locales/ja/101-Hello-world/hello-11.md new file mode 100644 index 0000000..18fc813 --- /dev/null +++ b/locales/ja/101-Hello-world/hello-11.md @@ -0,0 +1,5 @@ +XGo の Hello World には3つの書き方があります。 +--- +### 第1の方法:コマンドスタイル +--- +これは私たちが推奨する書き方で、非常に理解しやすいです。特に小中学生にとって、コマンドは最も理解しやすいロジックであり、関数呼び出しよりもはるかに簡単です。 diff --git a/locales/ja/101-Hello-world/hello-12.md b/locales/ja/101-Hello-world/hello-12.md new file mode 100644 index 0000000..939a4e8 --- /dev/null +++ b/locales/ja/101-Hello-world/hello-12.md @@ -0,0 +1 @@ +コマンドスタイルへの好みを強調するために、`println` のエイリアスとして `echo` を導入しました: diff --git a/locales/ja/101-Hello-world/hello-2.md b/locales/ja/101-Hello-world/hello-2.md new file mode 100644 index 0000000..98e58e1 --- /dev/null +++ b/locales/ja/101-Hello-world/hello-2.md @@ -0,0 +1,3 @@ +## 第2の方法:関数呼び出しスタイル +--- +この書き方は Python に似ています。この書き方を理解するための基礎は、関数呼び出しとは何かを理解することです。これはそれほど難しくありません。特に中学生が数学の授業で関数(sin など)を学んでいれば、比較的理解しやすいでしょう。ほとんどのプログラミング言語もこの標準的な関数呼び出し構文をサポートしています。 diff --git a/locales/ja/101-Hello-world/hello-3.md b/locales/ja/101-Hello-world/hello-3.md new file mode 100644 index 0000000..86855b6 --- /dev/null +++ b/locales/ja/101-Hello-world/hello-3.md @@ -0,0 +1,13 @@ +## 第3の方法:ソフトウェアエンジニアリングスタイル +--- +これは Go 言語から継承された標準的なソフトウェアエンジニアリングの書き方です。初心者には理解しにくいかもしれません。なぜなら、関数(func)とは何か、パッケージ(package)とは何かを理解する必要があるからです。もちろん、機能分解やチームコラボレーションの基本的なロジックを構築し始められるという利点もあります。 +--- +では、XGo を体験するにはどうすればよいでしょうか? +--- +最も簡単な方法は、XGo Playground に直接アクセスして体験することです: +--- +* https://play.xgo.dev +--- +基本的な文法を学んでいる初期段階では、この方法で十分です。 +--- +XGo をローカルにインストールする方法については、後ほど説明します。 diff --git a/locales/ja/102-Values/values.md b/locales/ja/102-Values/values.md new file mode 100644 index 0000000..7a6078d --- /dev/null +++ b/locales/ja/102-Values/values.md @@ -0,0 +1,9 @@ +XGo には文字列、整数、浮動小数点数、ブール値など、さまざまな値の型があります。 +--- +以下はいくつかの基本的な例です。 +--- +文字列は `+` で連結できます。 +--- +整数と浮動小数点数。 +--- +ブール値と、一般的なブール演算子。 diff --git a/locales/ja/103-Constants/constants.md b/locales/ja/103-Constants/constants.md new file mode 100644 index 0000000..3c30f58 --- /dev/null +++ b/locales/ja/103-Constants/constants.md @@ -0,0 +1,11 @@ +XGo は文字、文字列、ブール値、および数値型の_定数_をサポートしています。 +--- +`const` は定数値を宣言するために使用されます。 +--- +`const` 文は `var` 文が使える場所ならどこでも使えます。 +--- +定数式は任意精度の算術演算を行うことができます。 +--- +数値定数は、明示的な変換などによって型が与えられるまで、型を持ちません。 +--- +数値は、変数への代入や関数呼び出しなど、特定の型を必要とするコンテキストで使用することで型を得ることができます。例えば、ここでは `math.sin` が `float64` 型の引数を期待しています。 diff --git a/locales/ja/104-Variables/vars.md b/locales/ja/104-Variables/vars.md new file mode 100644 index 0000000..8c47fc6 --- /dev/null +++ b/locales/ja/104-Variables/vars.md @@ -0,0 +1,11 @@ +XGo では、_変数_ は明示的に宣言され、コンパイラはそれらを使って関数呼び出しの型の正しさなどをチェックします。 +--- +`var` は1つ以上の変数を宣言するために使用されます。 +--- +複数の変数を一度に宣言することができます。 +--- +XGo は初期化された変数の型を自動的に推論します。 +--- +初期化なしで宣言された変数には、_ゼロ値_ が設定されます。例えば、`int` のゼロ値は `0` です。 +--- +`:=` 構文は変数の宣言と初期化を同時に行う省略記法です。例えばこの場合、`var f string = "apple"` と同等です。 diff --git a/locales/ja/105-Assignments/assign-1.md b/locales/ja/105-Assignments/assign-1.md new file mode 100644 index 0000000..a82e7bf --- /dev/null +++ b/locales/ja/105-Assignments/assign-1.md @@ -0,0 +1,3 @@ +### 代入 +--- +`=` は代入に使用されます。1行で複数の変数の値を変更できます。この方法により、中間変数を使わずに値を交換することができます。 diff --git a/locales/ja/105-Assignments/assign-2.md b/locales/ja/105-Assignments/assign-2.md new file mode 100644 index 0000000..7333db8 --- /dev/null +++ b/locales/ja/105-Assignments/assign-2.md @@ -0,0 +1,3 @@ +### `=` と `:=` +--- +`:=` は変数の宣言と初期化に使用されます。1行で複数の変数を宣言して初期化することができます。 diff --git a/locales/ja/106-Types/types-2.md b/locales/ja/106-Types/types-2.md new file mode 100644 index 0000000..a6010b6 --- /dev/null +++ b/locales/ja/106-Types/types-2.md @@ -0,0 +1,7 @@ +### XGo は有理数のビルトインサポートを備えています: + + +XGo の基本型として有理数を導入しました。有理数リテラルを示すためにサフィックス `r` を使用します。例えば、`1r << 200` は 2^200 に等しい値を持つ大きな整数を意味します。 + +デフォルトでは、`1r` の型は bigint です。 +また、`4/5r` は有理定数 4/5 を表し、その型は bigrat です。 diff --git a/locales/ja/106-Types/types.md b/locales/ja/106-Types/types.md new file mode 100644 index 0000000..eede0a2 --- /dev/null +++ b/locales/ja/106-Types/types.md @@ -0,0 +1,19 @@ +### XGo の基本型 +
+bool
+int8    int16   int32   int    int64    int128
+uint8   uint16  uint32  uint   uint64   uint128
+uintptr(C 言語の size_t に類似)
+byte(uint8 のエイリアス)
+rune(int32 のエイリアス、Unicode コードポイントを表す)
+string
+float32 float64
+complex64 complex128
+bigint bigrat
+unsafe.Pointer(C 言語の void* に類似)
+any(Go の interface{} のエイリアス)
+
+ +この例では複数の型の変数を示すとともに、変数宣言が import 文と同様にブロックとして「グループ化」できることも示しています。 +--- +int、uint、uintptr 型は通常、32ビットシステムでは32ビット幅、64ビットシステムでは64ビット幅です。整数値が必要な場合は、固定サイズまたは符号なし整数型を使用する特定の理由がない限り、int を使用すべきです。 diff --git a/locales/ja/107-Integers/integers.md b/locales/ja/107-Integers/integers.md new file mode 100644 index 0000000..54ca416 --- /dev/null +++ b/locales/ja/107-Integers/integers.md @@ -0,0 +1,8 @@ +int 型は整数を表し、正の数にも負の数にもなります。int 型のサイズはプラットフォームに依存し、32ビットまたは64ビットのいずれかです。int8、int16、int32、int64、int128 など特定のサイズを持つ整数型もありますが、特定のサイズが必要でない限り int 型を使用すべきです。 + +uint 型は正の整数を表します。uint 型のサイズはプラットフォームに依存し、32ビットまたは64ビットのいずれかです。uint8、uint16、uint32、uint64、uint128 など特定のサイズを持つ符号なし整数型もありますが、特定のサイズが必要でない限り uint 型を使用すべきです。 + +int の値 20 は、16進数 (0x14)、8進数 (0o24)、2進数 (0b0010100) でも表現できます。 +uint には uint リテラルがなく、すべての整数リテラルは int 値として扱われます。 + +XGo は数値の区切り文字として `_` の使用もサポートしており、bool から数値型への変換もサポートしています。以下の例をご覧ください。 diff --git a/locales/ja/108-Floating-Point-Numbers/numbers.md b/locales/ja/108-Floating-Point-Numbers/numbers.md new file mode 100644 index 0000000..4cbc02a --- /dev/null +++ b/locales/ja/108-Floating-Point-Numbers/numbers.md @@ -0,0 +1,8 @@ +XGo には float32 と float64 の2つの浮動小数点型があります。 +浮動小数点リテラルのデフォルトの型は float64 です。 + +浮動小数点数は10進数の値を正確に表現できません。通貨やその他正確な10進数表現が必要な値には使用しないでください! + +== や != を使って浮動小数点数を比較することはできますが、推奨されません。浮動小数点数の不正確な性質により、等しいはずの2つの浮動小数点値が等しくならない場合があります。代わりに、最小許容偏差を定義し、2つの浮動小数点数の差がその値より小さいかどうかを確認してください。この最小値(イプシロンと呼ばれることもあります)は、精度の要件によって異なります。 + +浮動小数点リテラルは10の累乗でも宣言でき、値が0の浮動小数点変数を0で除算すると NaN(非数)が返されます。 diff --git a/locales/ja/109-Complex-Numbers/complex-1.md b/locales/ja/109-Complex-Numbers/complex-1.md new file mode 100644 index 0000000..35f0cec --- /dev/null +++ b/locales/ja/109-Complex-Numbers/complex-1.md @@ -0,0 +1,3 @@ +### 複素数の初期化 +XGo には complex64 と complex128 の2つの複素数型があります。 +複素数の初期化はとても簡単です。コンストラクタまたは初期化構文を使用できます。 diff --git a/locales/ja/109-Complex-Numbers/complex-2.md b/locales/ja/109-Complex-Numbers/complex-2.md new file mode 100644 index 0000000..a1e6f54 --- /dev/null +++ b/locales/ja/109-Complex-Numbers/complex-2.md @@ -0,0 +1,2 @@ +### 複素数の構成要素 +複素数には実部と虚部の2つの部分があります。それらを取得するために関数を使用します。 diff --git a/locales/ja/109-Complex-Numbers/complex-3.md b/locales/ja/109-Complex-Numbers/complex-3.md new file mode 100644 index 0000000..cc10384 --- /dev/null +++ b/locales/ja/109-Complex-Numbers/complex-3.md @@ -0,0 +1,4 @@ +### 複素数の演算 +複素数変数は加算、減算、乗算、除算など、あらゆる演算を行うことができます。 + +複素数に対する数学演算を実行する例を見てみましょう。 diff --git a/locales/ja/110-Booleans/boolean-1.md b/locales/ja/110-Booleans/boolean-1.md new file mode 100644 index 0000000..6603a42 --- /dev/null +++ b/locales/ja/110-Booleans/boolean-1.md @@ -0,0 +1 @@ +bool 型はブール変数を表します。bool 型の変数は true または false の2つの値のいずれかしか持つことができません。bool のゼロ値は false です。 diff --git a/locales/ja/110-Booleans/boolean-2.md b/locales/ja/110-Booleans/boolean-2.md new file mode 100644 index 0000000..4561bc8 --- /dev/null +++ b/locales/ja/110-Booleans/boolean-2.md @@ -0,0 +1 @@ +XGo では、bool を数値型に変換することができます。 diff --git a/locales/ja/111-Strings/strings.md b/locales/ja/111-Strings/strings.md new file mode 100644 index 0000000..fc9e005 --- /dev/null +++ b/locales/ja/111-Strings/strings.md @@ -0,0 +1,21 @@ +文字列は変更できないバイトのシーケンスです。文字列は任意のデータを含むことができ、通常はテキストを保持するために使用されます。 + +一般的にダブルクォート "" を使って文字列を定義しますが、特定の文字には特別な意味があることに注意してください。これらはエスケープ文字と呼ばれ、以下のものがあります: + +
+  \n:改行
+  \r:復帰
+  \t:タブ
+  \u または \U:Unicode
+  \:バックスラッシュ
+
+--- +文字列が占めるバイト数を知りたい場合は、XGo の組み込み関数を使って計算できます: +--- +文字列を定義する場合、構文は以下の通りです: +--- ++ を使って2つの文字列を連結し、後の文字列を前の文字列の末尾に追加できます。 +--- +複数行の文字列を定義したい場合、XGo はそれもサポートしています。従来の "" では行をまたぐことができませんが、バッククォートを使えば複数行の文字列を定義できます:` +--- +バッククォート間のコードはエディタによって特殊文字として認識されず、文字列の一部としてのみ扱われます。 diff --git a/locales/ja/112-Rational-Numbers/rational-1.md b/locales/ja/112-Rational-Numbers/rational-1.md new file mode 100644 index 0000000..f444d62 --- /dev/null +++ b/locales/ja/112-Rational-Numbers/rational-1.md @@ -0,0 +1,17 @@ +XGo には bigint、bigrat、bigfloat を含む複数の有理数型があります。 +以下は bigint 型に関するいくつかの基本的な例です。 +--- +### bigint 型変数の宣言と代入 +--- +XGo 言語はキーワード "var" を使って変数を宣言します。 +--- +整数有理変数は宣言時に値を代入できます。 +(1r<<65) の値は 2 の 65 乗に等しくなります。 +--- +注意: +未代入の有理変数の初期値は 0 ではなく `` です。 + +期待される結果: +bint1: `` +bint2: `` +bint3: `36893488147419103232` diff --git a/locales/ja/113-If/Else/if-else.md b/locales/ja/113-If/Else/if-else.md new file mode 100644 index 0000000..38b729e --- /dev/null +++ b/locales/ja/113-If/Else/if-else.md @@ -0,0 +1,9 @@ +XGo での `if` と `else` による分岐処理は非常にシンプルです。 +--- +これは基本的な例です。 +--- +else なしの `if` 文も使えます。 +--- +条件文の前に先行文を置くことができます。その文で宣言された変数はすべての分岐で利用可能です。 +--- +XGo では条件式を丸括弧で囲む必要はありませんが、波括弧は必須です。 diff --git a/locales/ja/113-Switch/switch-1.md b/locales/ja/113-Switch/switch-1.md new file mode 100644 index 0000000..5a548a2 --- /dev/null +++ b/locales/ja/113-Switch/switch-1.md @@ -0,0 +1,11 @@ +_Switch 文_ は多くの分岐にまたがる条件式を表現するために使用されます。 +--- +これは基本的な `switch` の例です。 +--- +同じ `case` 文でカンマ区切りで複数の式を使用できます。この例ではオプションの `default` ケースも使用しています。 +--- +式なしの `switch` は if/else ロジックを表現する別の方法です。ここでは `case` 式が定数でなくてもよいことも示しています。 +--- +XGo の switch はデフォルトで各 case の末尾で自動的に break します。fallthrough を使うと、後続の case のコードを強制的に実行できます。 +--- +`fallthrough` 付きの `switch`: diff --git a/locales/ja/114-For/for-0.md b/locales/ja/114-For/for-0.md new file mode 100644 index 0000000..7f157a6 --- /dev/null +++ b/locales/ja/114-For/for-0.md @@ -0,0 +1,2 @@ +### For ループ +XGo にはループキーワードが1つだけあります:for。ただし、複数の形式があります。 diff --git a/locales/ja/114-For/for-1.md b/locales/ja/114-For/for-1.md new file mode 100644 index 0000000..9bf4513 --- /dev/null +++ b/locales/ja/114-For/for-1.md @@ -0,0 +1,6 @@ +### 1、for/in +これは最も一般的な形式です。スライス、マップ、数値範囲、またはカスタムイテレータと組み合わせて使用できます。 + +`for value in arr/map` 形式は、スライスやマップの要素を走査するために使用されます。 + +インデックスが必要な場合は、`for index, value in arr` という代替形式を使用できます。 diff --git a/locales/ja/114-For/for-2.md b/locales/ja/114-For/for-2.md new file mode 100644 index 0000000..a7d4894 --- /dev/null +++ b/locales/ja/114-For/for-2.md @@ -0,0 +1,2 @@ +### 2. Range for +for ループで範囲式 (start:end:step) を使用できます。 diff --git a/locales/ja/114-For/for-3.md b/locales/ja/114-For/for-3.md new file mode 100644 index 0000000..038a8d7 --- /dev/null +++ b/locales/ja/114-For/for-3.md @@ -0,0 +1,2 @@ +### 3、for/in/if +すべての for/in 形式のループにはオプションの if 条件を付けることができます。 diff --git a/locales/ja/114-For/for-4.md b/locales/ja/114-For/for-4.md new file mode 100644 index 0000000..bcfcf1c --- /dev/null +++ b/locales/ja/114-For/for-4.md @@ -0,0 +1,2 @@ +### 4、条件付き for +条件を省略すると、無限ループになります。break または return を使ってループを終了できます。 diff --git a/locales/ja/114-For/for-5.md b/locales/ja/114-For/for-5.md new file mode 100644 index 0000000..108519d --- /dev/null +++ b/locales/ja/114-For/for-5.md @@ -0,0 +1,2 @@ +### 5、C スタイルの for +最後に、伝統的な C スタイルの for ループです。while 形式よりも安全です。while ではカウンタの更新を忘れて無限ループに陥りやすいためです。 diff --git a/locales/ja/114-For/for-6.md b/locales/ja/114-For/for-6.md new file mode 100644 index 0000000..afaa132 --- /dev/null +++ b/locales/ja/114-For/for-6.md @@ -0,0 +1,4 @@ +### break と continue +キーボードを使ったりコンピュータの電源を切ったりせずに、無限 for ループから抜け出すにはどうすればよいでしょうか?それが break 文の役割です。他の言語の break 文と同様に、ループを即座に終了します。もちろん、無限 for 文だけでなく、どの for 文でも break を使用できます。 + +XGo には continue キーワードもあり、for ループの本体の残りをスキップして、次のイテレーションに直接進みます。技術的には、continue 文は必須ではありません。 diff --git a/locales/ja/114-For/for-7.md b/locales/ja/114-For/for-7.md new file mode 100644 index 0000000..76c0321 --- /dev/null +++ b/locales/ja/114-For/for-7.md @@ -0,0 +1,2 @@ +### "for" 文へのラベル付け +デフォルトでは、break と continue キーワードはそれらを直接含む for ループに作用します。ネストされた for ループがあり、外側のループのイテレーションを終了またはスキップしたい場合はどうすればよいでしょうか?例を見てみましょう。文字列走査プログラムを修正して、文字 "l" に遭遇した時点でその文字列の走査を停止するようにします。 diff --git a/locales/ja/116-Arrays/arrays1.md b/locales/ja/116-Arrays/arrays1.md new file mode 100644 index 0000000..7a1138a --- /dev/null +++ b/locales/ja/116-Arrays/arrays1.md @@ -0,0 +1,13 @@ +XGo では、_配列_ は特定の長さを持つ、同じ型の要素からなる番号付きのシーケンスです。 +--- +### 一次元配列の宣言 +--- +ここでは、ちょうど5つの `int` を格納できる配列 `a` を作成します。要素の型と長さは配列の型の一部です。デフォルトでは、配列はゼロ値で初期化され、`int` の場合は `0` です。 +--- +`array[index] = value` 構文でインデックス位置に値を設定し、`array[index]` で値を取得できます。 +--- +組み込み関数 `len` は配列の長さを返します。 +--- +この構文を使って、1行で配列の宣言と初期化を行えます。 +--- +配列の長さを手動で書きたくない場合は、この方法を使って、コンパイラに配列の長さを自動計算させることができます。 diff --git a/locales/ja/116-Arrays/arrays2.md b/locales/ja/116-Arrays/arrays2.md new file mode 100644 index 0000000..41dd4c6 --- /dev/null +++ b/locales/ja/116-Arrays/arrays2.md @@ -0,0 +1,5 @@ +### 多次元配列の宣言 +--- +配列の型は一次元ですが、型を組み合わせて多次元データ構造を構築できます。 +--- +さらに多くの次元を宣言する必要がある場合は、自分で拡張できます。例えば、2*2*3 の三次元配列を宣言できます。 diff --git a/locales/ja/116-Arrays/arrays3.md b/locales/ja/116-Arrays/arrays3.md new file mode 100644 index 0000000..1830610 --- /dev/null +++ b/locales/ja/116-Arrays/arrays3.md @@ -0,0 +1,7 @@ +#### ヒント +--- +XGo で配列の内容を宣言しない場合、コンパイラは自動的に配列を 0 で初期化します。bool 型の配列の場合、初期値は false です。 +--- +その他の配列値の型について、XGo は文字列、整数、浮動小数点数、ブール値などをサポートしています。詳細はこちらの章を参照してください: +--- +* https://tutorial.xgo.dev/values diff --git a/locales/ja/117-Slices/slices-01.md b/locales/ja/117-Slices/slices-01.md new file mode 100644 index 0000000..ca160bd --- /dev/null +++ b/locales/ja/117-Slices/slices-01.md @@ -0,0 +1,4 @@ +### XGo スタイルのスライスリテラル +スライスは同じ型のデータ要素のコレクションです。スライスリテラルは角括弧で囲まれた式のリストです。個々の要素にはインデックス式を使ってアクセスできます。インデックスは 0 から始まります。 + +XGo では、len メソッドで直接スライスの長さを取得でき、スライスリテラルの型変換も可能です: diff --git a/locales/ja/117-Slices/slices-02.md b/locales/ja/117-Slices/slices-02.md new file mode 100644 index 0000000..c532bd7 --- /dev/null +++ b/locales/ja/117-Slices/slices-02.md @@ -0,0 +1,6 @@ +### スライス +配列は固定サイズです。一方、スライスは配列の要素に対する動的サイズの柔軟なビューです。実際には、スライスの方が配列よりもはるかに一般的に使用されます。 + +型 []T は要素型が T のスライスです。 + +スライスは、コロンで区切られた2つのインデックス(下限と上限)を指定することで形成されます:a[low : high]。これは半開区間を選択し、最初の要素を含みますが、最後の要素は含みません。 diff --git a/locales/ja/117-Slices/slices-03.md b/locales/ja/117-Slices/slices-03.md new file mode 100644 index 0000000..492e4d7 --- /dev/null +++ b/locales/ja/117-Slices/slices-03.md @@ -0,0 +1,6 @@ +### スライスは配列への参照のようなもの +スライスはデータを格納しません。基底配列の一部分を記述するだけです。 + +スライスの要素を変更すると、基底配列の対応する要素が変更されます。 + +同じ基底配列を共有する他のスライスにもその変更が反映されます。 diff --git a/locales/ja/117-Slices/slices-04.md b/locales/ja/117-Slices/slices-04.md new file mode 100644 index 0000000..e4b3409 --- /dev/null +++ b/locales/ja/117-Slices/slices-04.md @@ -0,0 +1,8 @@ +### スライスリテラル +スライスリテラルは長さのない配列リテラルのようなものです。 +
+これは配列リテラルです:
+[3]bool{true, true, false}
+そして以下は上記と同じ配列を作成し、それを参照するスライスを構築します:
+[]bool{true, true, false}
+
diff --git a/locales/ja/117-Slices/slices-05.md b/locales/ja/117-Slices/slices-05.md new file mode 100644 index 0000000..bfaf651 --- /dev/null +++ b/locales/ja/117-Slices/slices-05.md @@ -0,0 +1,13 @@ +### スライスのデフォルト値 +スライス操作を行う際、上限または下限を省略してデフォルト値を使用できます。 + +下限のデフォルト値は 0 で、上限のデフォルト値はスライスの長さです。 +
+配列
+var a [10]int
+に対して、以下のスライス式は等価です:
+a[0:10]
+a[:10]
+a[0:]
+a[:]
+
diff --git a/locales/ja/117-Slices/slices-06.md b/locales/ja/117-Slices/slices-06.md new file mode 100644 index 0000000..cc63038 --- /dev/null +++ b/locales/ja/117-Slices/slices-06.md @@ -0,0 +1,10 @@ +### スライスの長さと容量 +スライスには長さと容量の両方があります。 + +スライスの長さは、それが含む要素の数です。 + +スライスの容量は、スライスの最初の要素から数えた、基底配列の末尾までの要素数です。 + +スライス s の長さと容量は、式 len(s) と cap(s) で取得できます。 + +十分な容量があれば、再スライスによってスライスの長さを拡張できます。サンプルプログラムのスライス操作の1つを変更して、容量を超えるようにしてみてください。何が起こるか見てみましょう。 diff --git a/locales/ja/117-Slices/slices-07.md b/locales/ja/117-Slices/slices-07.md new file mode 100644 index 0000000..72b6aa0 --- /dev/null +++ b/locales/ja/117-Slices/slices-07.md @@ -0,0 +1,4 @@ +### nil スライス +スライスのゼロ値は nil です。 + +nil スライスの長さと容量は 0 で、基底配列を持ちません。 diff --git a/locales/ja/117-Slices/slices-08.md b/locales/ja/117-Slices/slices-08.md new file mode 100644 index 0000000..08d6604 --- /dev/null +++ b/locales/ja/117-Slices/slices-08.md @@ -0,0 +1,12 @@ +### make によるスライスの作成 +スライスは組み込みの make 関数で作成できます。これは動的サイズの配列を作成する方法です。 + +make 関数はゼロ値の配列を割り当て、その配列を参照するスライスを返します: + +
+a := make([]int, 5)			// len(a)=5
+容量を指定するには、make に第3引数を渡します:
+b := make([]int, 0, 5) 		// len(b)=0, cap(b)=5
+b = b[:cap(b)] 				// len(b)=5, cap(b)=5
+b = b[1:]      				// len(b)=4, cap(b)=4
+
diff --git a/locales/ja/117-Slices/slices-09.md b/locales/ja/117-Slices/slices-09.md new file mode 100644 index 0000000..93723f7 --- /dev/null +++ b/locales/ja/117-Slices/slices-09.md @@ -0,0 +1,3 @@ +### スライスのスライス +スライスは他のスライスを含め、あらゆる型を含むことができます。 +三目並べの盤面を作成します。 diff --git a/locales/ja/117-Slices/slices-10.md b/locales/ja/117-Slices/slices-10.md new file mode 100644 index 0000000..becfd67 --- /dev/null +++ b/locales/ja/117-Slices/slices-10.md @@ -0,0 +1,10 @@ +### スライスへの要素追加 +スライスに新しい要素を追加するのは一般的な操作なので、XGo は組み込みの append 関数を提供しています。組み込みパッケージのドキュメントに append の使い方が記載されています。 + +func append(s []T, vs ...T) []T + +append の最初の引数 s は型 T のスライスで、残りはスライスに追加する T 型の値です。 + +append の戻り値は、元のスライスのすべての要素と新しく提供された値を含むスライスです。 + +s の基底配列が小さすぎてすべての値を格納できない場合、より大きな配列が割り当てられます。返されるスライスは新しく割り当てられた配列を指します。 diff --git a/locales/ja/117-Slices/slices-11.md b/locales/ja/117-Slices/slices-11.md new file mode 100644 index 0000000..9080ce2 --- /dev/null +++ b/locales/ja/117-Slices/slices-11.md @@ -0,0 +1,4 @@ +### Range +for ループの range 形式は、スライスやマップを走査します。 + +スライスを走査する際、各イテレーションで2つの値が返されます。1つ目はインデックスで、2つ目はそのインデックスの要素のコピーです。 diff --git a/locales/ja/117-Slices/slices-12.md b/locales/ja/117-Slices/slices-12.md new file mode 100644 index 0000000..712a09a --- /dev/null +++ b/locales/ja/117-Slices/slices-12.md @@ -0,0 +1,9 @@ +### Range(続き) + +
+_ に代入することでインデックスまたは値をスキップできます。
+for i, _ := range pow
+for _, value := range pow
+インデックスだけが必要な場合は、2番目の変数を省略できます。
+for i := range pow
+
diff --git a/locales/ja/118-Maps/map-0.md b/locales/ja/118-Maps/map-0.md new file mode 100644 index 0000000..3347c2c --- /dev/null +++ b/locales/ja/118-Maps/map-0.md @@ -0,0 +1,17 @@ +_Maps_ は XGo に組み込まれた[連想データ型](http://en.wikipedia.org/wiki/Associative_array)です(他の言語では _ハッシュ_ や _辞書_ と呼ばれることもあります)。 +--- +### Map の基本 +--- +`make(map[key-type]val-type)` を使って空のマップを作成します。 +--- +一般的な `name[key] = val` 構文でキーと値のペアを設定します。 +--- +`println` などでマップを出力すると、すべてのキーと値のペアが表示されます。 +--- +`name[key]` でキーに対応する値を取得します。 +--- +組み込み関数 `len` をマップに使用すると、キーと値のペアの数を返します。 +--- +組み込み関数 `delete` はマップからキーと値のペアを削除します。 +--- +マップから値を取得する際のオプションの2番目の戻り値は、そのキーがマップに存在するかどうかを示します。これは、`0` や `""` のようなゼロ値のキーと欠落しているキーを区別するために使用できます。ここでは値自体は不要なので、_ブランク識別子_ `_` で無視しています。 diff --git a/locales/ja/118-Maps/map-1.md b/locales/ja/118-Maps/map-1.md new file mode 100644 index 0000000..f830a7e --- /dev/null +++ b/locales/ja/118-Maps/map-1.md @@ -0,0 +1,11 @@ +### XGo スタイルの Map リテラル +--- +map[string]int +--- +map[string]float64 +--- +map[string]interface{} +--- +map[int]interface{} +--- +空のマップの型は map[string]interface{} です diff --git a/locales/ja/118-Maps/map-2.md b/locales/ja/118-Maps/map-2.md new file mode 100644 index 0000000..c0ea496 --- /dev/null +++ b/locales/ja/118-Maps/map-2.md @@ -0,0 +1,13 @@ +### Go スタイルの Map リテラル +--- +以下の構文を使って、1行でマップの宣言と初期化を行うこともできます。 +--- +{"Hello": 1, "xsw": 3} +--- +{"Hello": 1, "xsw": 3.4} +--- +{"Hello": 1, "xsw": "XGo"} +--- +{1: 1.4, 3: "XGo"} +--- +{} diff --git a/locales/ja/119-Structs/struct-1.md b/locales/ja/119-Structs/struct-1.md new file mode 100644 index 0000000..73ee145 --- /dev/null +++ b/locales/ja/119-Structs/struct-1.md @@ -0,0 +1,9 @@ +Map はある種のデータを格納するのに便利ですが、制限があります。特定のキーのみを許可するようにマップを制限する方法がないため、API を定義できません。マップ内のすべての値も同じ型でなければなりません。これらの理由から、マップは関数間でデータを受け渡す理想的な方法ではありません。関連するデータをグループ化したい場合は、構造体を定義すべきです。 + +構造体型は、キーワード type、構造体型の名前、キーワード struct、および波括弧({})のペアで定義します。波括弧内に構造体のフィールドを列挙します。var 宣言で変数名を先に、変数型を後に書くのと同様に、構造体のフィールドもフィールド名を先に、フィールド型を後に書きます。 + +また、マップリテラルとは異なり、構造体宣言ではフィールド間にカンマがないことにも注意してください。構造体型は関数の内部でも外部でも定義できます。関数内で定義された構造体型は、その関数内でのみ使用できます。技術的には、任意のブロックレベルで構造体を定義できます。 + +構造体型を宣言すれば、その型の変数を定義できます。 +--- +ここでは var 宣言を使用しています。fred に値が代入されていないため、person 構造体型のゼロ値が設定されます。ゼロ値の構造体では、すべてのフィールドがそのフィールドのゼロ値に設定されます。 diff --git a/locales/ja/119-Structs/struct-2.md b/locales/ja/119-Structs/struct-2.md new file mode 100644 index 0000000..8fd9b99 --- /dev/null +++ b/locales/ja/119-Structs/struct-2.md @@ -0,0 +1,4 @@ +### 空でない構造体リテラルには2つの異なるスタイルがあります。 +構造体リテラルは、波括弧内のカンマ区切りのフィールド値のリストとして指定できます: +--- +この構造体リテラル形式を使用する場合、構造体のすべてのフィールドに値を指定する必要があり、値は構造体定義で宣言された順序で割り当てられます。 diff --git a/locales/ja/119-Structs/struct-3.md b/locales/ja/119-Structs/struct-3.md new file mode 100644 index 0000000..e2cc9b0 --- /dev/null +++ b/locales/ja/119-Structs/struct-3.md @@ -0,0 +1,4 @@ + +2番目の構造体リテラルスタイルはマップリテラルスタイルに似ています: +--- +構造体のフィールド名を使って値を指定できます。このスタイルを使用すると、キーを省略したり、任意の順序でフィールドを指定したりできます。指定されなかったフィールドはゼロ値に設定されます。2つの構造体リテラルスタイルを混在させることはできません。すべてのフィールドをキー名で指定するか、すべてキー名なしで指定するかのいずれかです。すべてのフィールドが常に指定される小さな構造体の場合は、より簡単な構造体リテラルスタイルで問題ありません。その他の場合は、キー名を使用してください。より冗長ですが、構造体定義を参照しなくても、どの値がどのフィールドに割り当てられているかが明確になります。また、メンテナンス性も向上します。フィールド名を使わずに構造体を初期化し、将来のバージョンで構造体にフィールドが追加された場合、コードはコンパイルできなくなります。 diff --git a/locales/ja/119-Structs/struct-4.md b/locales/ja/119-Structs/struct-4.md new file mode 100644 index 0000000..ab7291e --- /dev/null +++ b/locales/ja/119-Structs/struct-4.md @@ -0,0 +1,4 @@ + +構造体のフィールドにはドット記法を使ってアクセスします: +--- +マップの読み書きにブラケットを使うのと同様に、構造体フィールドの読み書きにはドット記法を使用します。 diff --git a/locales/ja/119-Structs/struct-5.md b/locales/ja/119-Structs/struct-5.md new file mode 100644 index 0000000..8cad5c8 --- /dev/null +++ b/locales/ja/119-Structs/struct-5.md @@ -0,0 +1,6 @@ +### 匿名構造体 +構造体型に名前を付けずに、変数がある構造体型を実装することを宣言することもできます。これは匿名構造体と呼ばれます。 +--- +この例では、変数 person と pet の型は匿名構造体です。匿名構造体のフィールドの代入と読み取りは、名前付き構造体型と同様に行えます。名前付き構造体のインスタンスを構造体リテラルで初期化できるのと同様に、匿名構造体でも同じことができます。単一のインスタンスにしか関連付けられないデータ型がいつ必要になるか疑問に思うかもしれません。匿名構造体が便利な一般的な状況が2つあります。1つ目は、外部データを構造体に変換するか、構造体を外部データ(JSON や protocol buffers など)に変換する場合です。これはデータのマーシャリングとアンマーシャリングと呼ばれます。 + +テストを書くことも、匿名構造体がよく使われるもう1つの場面です。 diff --git a/locales/ja/119-Structs/struct-6.md b/locales/ja/119-Structs/struct-6.md new file mode 100644 index 0000000..d79fffe --- /dev/null +++ b/locales/ja/119-Structs/struct-6.md @@ -0,0 +1,9 @@ +### 構造体の比較と変換 +構造体が比較可能かどうかは、そのフィールドに依存します。比較可能な型のみで構成される構造体は比較可能です。スライスやマップフィールドを持つ構造体は比較できません(後の章で説明するように、関数やチャネルフィールドも構造体を比較不可能にします)。 + +Python や Ruby とは異なり、== や != を比較不可能な構造体で動作するように再定義するためにオーバーライドできるマジックメソッドはありません。もちろん、構造体を比較するための独自の関数を書くことはできます。 + +XGo が異なる基本型の変数間の比較を許可しないのと同様に、異なる型の構造体を表す変数間の比較も許可しません。両方の構造体のフィールドが同じ名前、順序、型を持つ場合、XGo はある構造体型から別の構造体型への型変換を実行できます。これが何を意味するか見てみましょう。以下の構造体が与えられたとします: +--- +secondPerson のインスタンスを firstPerson に型変換することはできますが、フィールドの順序が異なるため thirdPerson のインスタンスを firstPerson に変換することはできません。 +ただし、== を使って firstPerson のインスタンスと secondPerson や thirdPerson のインスタンスを比較することはできません。異なる型だからです。 diff --git a/locales/ja/119-Structs/struct-7.md b/locales/ja/119-Structs/struct-7.md new file mode 100644 index 0000000..3df5482 --- /dev/null +++ b/locales/ja/119-Structs/struct-7.md @@ -0,0 +1 @@ +匿名構造体にはこの点で小さな特殊ケースがあります:比較される2つの構造体変数のうち少なくとも1つが匿名構造体型である場合、両方の構造体のフィールドが同じ名前、順序、型を持つならば、型変換なしで比較できます。同様に、両方の構造体のフィールドが同じ名前、順序、型を持つ場合、名前付き構造体型と匿名構造体型の間で代入も可能です。 diff --git a/locales/ja/120-Pointers/pointer-1.md b/locales/ja/120-Pointers/pointer-1.md new file mode 100644 index 0000000..45b2d4c --- /dev/null +++ b/locales/ja/120-Pointers/pointer-1.md @@ -0,0 +1,6 @@ +ポインタはメモリアドレスを値として持つ変数です。ポインタはアンパサンド(& 文字)、すなわちアドレス演算子を使い、その後に変数名を続けて定義します。 + +ポインタの型は固定です。つまり、int へのポインタを作成した場合、それが指す値を変更することはできますが、float64 などの異なる型を格納するメモリアドレスを指すために使用することはできません。この制限は重要で、ポインタは単なるメモリアドレスではなく、特定の型の値を格納する可能性のあるメモリアドレスです。 + +ポインタの型は、ポインタの作成元の変数の型にアスタリスク(* 文字)を前置したものです。second という変数の型は *int です。これは first 変数にアドレス演算子を適用して作成されたもので、first の値は int 型です。*int という型を見たら、int 変数を格納するメモリアドレスを値として持つ変数であることがわかります。 + diff --git a/locales/ja/120-Pointers/pointer-2.md b/locales/ja/120-Pointers/pointer-2.md new file mode 100644 index 0000000..b5a0d7c --- /dev/null +++ b/locales/ja/120-Pointers/pointer-2.md @@ -0,0 +1,6 @@ +### ポインタの追跡 +「ポインタを追跡する」というフレーズは、ポインタが参照するメモリアドレスの値を読み取ることを意味し、アスタリスク(* 文字)を使って行います。アスタリスクは XGo にポインタを追跡してそのメモリ位置の値を取得するよう指示します。これはポインタのデリファレンス(参照解除)と呼ばれます。 +--- +最初の新しい文は新しい変数を定義しています。ここでは var キーワードを使って、変数の型が *int、つまり int 値へのポインタであることを強調しています。次の文は second 変数の値を新しい変数に代入しています。つまり、second と myNewPointer の両方の値が first の値のメモリ位置になります。どちらのポインタを追跡しても同じメモリ位置にアクセスするため、myNewPointer をインクリメントすると、second ポインタを追跡して得られる値にも影響します。 + +よくある誤解は、first と second 変数が同じ値を持つというものですが、実際にはそうではありません。ここには2つの値があります。1つは first という名前の変数でアクセスできる int 値です。もう1つは first の値のメモリ位置を格納する *int 値です。*int 値を追跡すると、格納されている int 値にアクセスできます。しかし、*int 値は、それ自体が値であるため、独立して使用できます。つまり、他の変数に代入したり、関数呼び出しの引数として使用したりできます。 diff --git a/locales/ja/120-Pointers/pointer-3.md b/locales/ja/120-Pointers/pointer-3.md new file mode 100644 index 0000000..0910493 --- /dev/null +++ b/locales/ja/120-Pointers/pointer-3.md @@ -0,0 +1,6 @@ +### ポインタのゼロ値の理解 +定義されているが値が代入されていないポインタのゼロ値は nil です。 + +ポインタ second は定義されていますが値で初期化されておらず、println 関数で出力されます。次にアドレス演算子を使って first 変数へのポインタが作成され、再び second の値が出力されます。 + +値が代入されていないポインタを追跡すると、ランタイムエラーが発生します。 diff --git a/locales/ja/120-Pointers/pointer-4.md b/locales/ja/120-Pointers/pointer-4.md new file mode 100644 index 0000000..8ffb977 --- /dev/null +++ b/locales/ja/120-Pointers/pointer-4.md @@ -0,0 +1,4 @@ +### ポインタへのポインタ +ポインタはメモリ位置を格納するため、別のポインタのメモリアドレスを値とするポインタを作成することができます。 +--- +ポインタチェーンを追跡する構文は少し扱いにくいことがあります。この場合、2つのアスタリスクが必要です。最初のアスタリスクはポインタをメモリ位置まで追跡し、second という名前の変数が格納している値、つまり *int 値を取得します。2番目のアスタリスクは second という名前のポインタを追跡し、first 変数が格納している値のメモリ位置にアクセスします。これはほとんどのプロジェクトで必要になることではありませんが、ポインタの動作原理やチェーンをたどってデータ値を取得する方法をよく確認できます。 diff --git a/locales/ja/121-For-Each/for-each-1.md b/locales/ja/121-For-Each/for-each-1.md new file mode 100644 index 0000000..ea51405 --- /dev/null +++ b/locales/ja/121-For-Each/for-each-1.md @@ -0,0 +1 @@ +XGo には foreach ループはありませんが、for ループを "foreach" のように使えます。`range` というキーワードがあり、for と range を組み合わせると、ループ内でキーまたは値を選べます。 diff --git a/locales/ja/121-For-Each/for-each-2.md b/locales/ja/121-For-Each/for-each-2.md new file mode 100644 index 0000000..473501d --- /dev/null +++ b/locales/ja/121-For-Each/for-each-2.md @@ -0,0 +1,6 @@ +### for-range ループ +for-range ループの興味深い点は、2つのループ変数が得られることです。1つ目の変数は走査対象のデータ構造内での位置で、2つ目はその位置の値です。2つのループ変数の慣用的な名前は、走査対象によって異なります。配列、スライス、または文字列を走査する場合、インデックスには通常 `i` を使います。マップを走査する場合は、`k`(key の略)が使われます。 + +2つ目の変数は値を表す `v` と名付けられることが多いですが、走査対象の値の型に基づいて名付けられることもあります。 + +キーにアクセスする必要がない場合は、変数名としてアンダースコア(_)を使用します。これは XGo にその値を無視させます。 diff --git a/locales/ja/121-For-Each/for-each-3.md b/locales/ja/121-For-Each/for-each-3.md new file mode 100644 index 0000000..ce489b5 --- /dev/null +++ b/locales/ja/121-For-Each/for-each-3.md @@ -0,0 +1,2 @@ +### マップの走査 +for-range でマップを走査するときには、いくつか興味深い点があります。 diff --git a/locales/ja/121-For-Range/range.md b/locales/ja/121-For-Range/range.md new file mode 100644 index 0000000..c1917a6 --- /dev/null +++ b/locales/ja/121-For-Range/range.md @@ -0,0 +1,11 @@ +`range` はさまざまなデータ構造の要素を走査します。これまでに学んだデータ構造で `range` をどのように使うか見てみましょう。 +--- +ここでは `range` を使ってスライス内の数値を合計しています。配列でも同様に使えます。 +--- +配列やスライスに対して `range` を使うと、各要素のインデックスと値の両方が提供されます。上記ではインデックスが不要だったため、空白識別子 `_` で無視しました。しかし、実際にインデックスが必要な場合もあります。 +--- +マップに対する `range` はキーと値のペアを走査します。 +--- +`range` はマップのキーだけを走査することもできます。 +--- +文字列に対する `range` は Unicode のコードポイントを走査します。1つ目の値は `rune` の開始バイトインデックスで、2つ目の値は `rune` 自体です。 diff --git a/locales/ja/121-List-Comprehension/listcompr.md b/locales/ja/121-List-Comprehension/listcompr.md new file mode 100644 index 0000000..e7812d3 --- /dev/null +++ b/locales/ja/121-List-Comprehension/listcompr.md @@ -0,0 +1,9 @@ +奇数と偶数のリストを生成します。 +--- +平方数 +--- +イテレーション +--- +文字列の分割 +--- +指定した文字で文字列を分割 diff --git a/locales/ja/201-Functions/funcs.md b/locales/ja/201-Functions/funcs.md new file mode 100644 index 0000000..e753c44 --- /dev/null +++ b/locales/ja/201-Functions/funcs.md @@ -0,0 +1,9 @@ +_関数_ は XGo の中核です。いくつかの異なる例を通じて関数について学びましょう。 +--- +これは 2 つの `int` 引数を受け取り、その合計を `int` として返す関数です。 +--- +XGo では明示的な return が必要です。つまり、最後の式の値が自動的に返されることはありません。 +--- +連続する複数のパラメータが同じ型を持つ場合、最後のパラメータで型を宣言するだけで、それ以前の同じ型のパラメータの型名を省略できます。 +--- +関数の呼び出しは、期待通り `name(args)` の形式で行います。 diff --git a/locales/ja/202-Multiple-Return-Values/multi-rets.md b/locales/ja/202-Multiple-Return-Values/multi-rets.md new file mode 100644 index 0000000..af7f5f2 --- /dev/null +++ b/locales/ja/202-Multiple-Return-Values/multi-rets.md @@ -0,0 +1,7 @@ +XGo は_複数の戻り値_を組み込みでサポートしています。この機能は慣用的な XGo コードで頻繁に使用されます。例えば、関数から結果とエラー値を同時に返す場合などです。 +--- +この関数シグネチャの `(int, int)` は、関数が 2 つの `int` を返すことを示しています。 +--- +ここでは、呼び出しから返された 2 つの異なる値を_多重代入_で使用しています。 +--- +戻り値の一部だけが必要な場合は、ブランク識別子 `_` を使用します。 diff --git a/locales/ja/203-Errors/errors.md b/locales/ja/203-Errors/errors.md new file mode 100644 index 0000000..6c7f2a9 --- /dev/null +++ b/locales/ja/203-Errors/errors.md @@ -0,0 +1,15 @@ +XGo では、明示的で独立した戻り値を通じてエラーを伝達するのが慣用的です。これは Java や Ruby のような例外を使用する言語や、C 言語で時々使われる単一の結果/エラー値をオーバーロードする方法とは異なります。XGo のアプローチにより、どの関数がエラーを返すかが容易に分かり、他の非エラータスクと同じ言語構造を使ってエラーを処理できます。 +--- +慣例として、エラーは最後の戻り値であり、組み込みインターフェースである `error` 型を持ちます。 +--- +`errors.New` は、指定されたエラーメッセージで基本的な `error` 値を構築します。 +--- +エラー位置の `nil` 値は、エラーがなかったことを示します。 +--- +カスタム型に `Error()` メソッドを実装することで、その型を `error` として使用できます。以下は上記の例のバリエーションで、引数エラーを明示的に表現するためにカスタム型を使用しています。 +--- +この場合、`&argError` 構文を使って新しい構造体を構築し、`arg` と `prob` の 2 つのフィールドに値を提供しています。 +--- +以下の 2 つのループは、エラーを返す各関数をテストしています。`if` 行でインラインエラーチェックを使用するのは、XGo コードでの一般的なイディオムです。 +--- +カスタムエラーのデータをプログラムで使用したい場合は、型アサーションによってエラーをカスタムエラー型のインスタンスとして取得する必要があります。 diff --git a/locales/ja/204-Function Values/func-values.md b/locales/ja/204-Function Values/func-values.md new file mode 100644 index 0000000..a12145c --- /dev/null +++ b/locales/ja/204-Function Values/func-values.md @@ -0,0 +1 @@ +関数も値です。他の値と同様に受け渡しできます。関数値は関数の引数や戻り値として使用できます。 diff --git a/locales/ja/205-Closures/closures.md b/locales/ja/205-Closures/closures.md new file mode 100644 index 0000000..78c4593 --- /dev/null +++ b/locales/ja/205-Closures/closures.md @@ -0,0 +1 @@ +XGo の関数はクロージャになれます。クロージャとは、関数本体の外部にある変数を参照する関数値です。この関数は参照先の変数にアクセスしたり代入したりできます。この意味で、関数はそれらの変数に「束縛」されています。例えば、adder 関数はクロージャを返します。各クロージャはそれぞれ独自の sum 変数に束縛されています。 diff --git a/locales/ja/205-Lambda-expressions/lambda-expressions-1.md b/locales/ja/205-Lambda-expressions/lambda-expressions-1.md new file mode 100644 index 0000000..2db8311 --- /dev/null +++ b/locales/ja/205-Lambda-expressions/lambda-expressions-1.md @@ -0,0 +1,3 @@ +ラムダ式は、関数に名前を付けずにインラインで定義したいときに使います。 +--- +以下の例は、XGo スタイルのラムダ式を示しています。より簡潔でわかりやすい書き方です。 diff --git a/locales/ja/205-Lambda-expressions/lambda-expressions-2.md b/locales/ja/205-Lambda-expressions/lambda-expressions-2.md new file mode 100644 index 0000000..14d469c --- /dev/null +++ b/locales/ja/205-Lambda-expressions/lambda-expressions-2.md @@ -0,0 +1 @@ +ラムダを定義するだけで、まだ実行しない場合は、識別子を省略するだけです。例えば: diff --git a/locales/ja/205-Lambda-expressions/lambda-expressions-3.md b/locales/ja/205-Lambda-expressions/lambda-expressions-3.md new file mode 100644 index 0000000..955bbb0 --- /dev/null +++ b/locales/ja/205-Lambda-expressions/lambda-expressions-3.md @@ -0,0 +1 @@ +ラムダを定義してすぐに実行したい場合は、識別子を省略し、関数本体の閉じ波括弧の後に括弧付きの引数リストを追加します。例えば: diff --git a/locales/ja/206-Recursion/recursion.md b/locales/ja/206-Recursion/recursion.md new file mode 100644 index 0000000..c4e6de1 --- /dev/null +++ b/locales/ja/206-Recursion/recursion.md @@ -0,0 +1,6 @@ +XGo プログラミング言語は再帰をサポートしています。つまり、関数が自分自身を呼び出すことができます。ただし、再帰を使用する場合、プログラマーは関数の終了条件を定義するよう注意する必要があります。そうしないと無限ループに陥ります。 +再帰関数は、階乗の計算やフィボナッチ数列の生成など、多くの数学的問題を解くのに非常に便利です。 +--- +この例では、再帰関数を使用して指定された数値の階乗を計算します。 +--- +この例では、再帰関数を使用して指定された数値のフィボナッチ数列を生成する方法を示します。 diff --git a/locales/ja/207-Variadic-Parameters/variadic.md b/locales/ja/207-Variadic-Parameters/variadic.md new file mode 100644 index 0000000..b3397d3 --- /dev/null +++ b/locales/ja/207-Variadic-Parameters/variadic.md @@ -0,0 +1 @@ +可変数の引数で呼び出される joinstr 関数は可変長引数関数と呼ばれます。joinstr 関数の宣言では、最後のパラメータの型の前に省略記号(…)が付いています。これは、この関数が任意の数の string 引数を受け取れることを示しています。joinstr(elements...) の呼び出しで ... を付けない場合、型が一致しないためコンパイルエラーになります。elements は string 型ではないからです。 diff --git a/locales/ja/208-Defer/defer-1.md b/locales/ja/208-Defer/defer-1.md new file mode 100644 index 0000000..7985ce4 --- /dev/null +++ b/locales/ja/208-Defer/defer-1.md @@ -0,0 +1,3 @@ +defer 文は、外側の関数が return するまで関数の実行を延期します。遅延呼び出しの引数は即座に評価されますが、関数呼び出しは外側の関数が return するまで実行されません。Defer は、さまざまなクリーンアップ操作を行う関数を簡潔にするために一般的に使用されます。 +--- +defer 文を使うと、ファイルを開いた直後に閉じることを考えられるため、関数内の return 文の数に関係なく、ファイルが確実に閉じられることが保証されます。 diff --git a/locales/ja/208-Defer/defer-2.md b/locales/ja/208-Defer/defer-2.md new file mode 100644 index 0000000..676343a --- /dev/null +++ b/locales/ja/208-Defer/defer-2.md @@ -0,0 +1,3 @@ +### defer のスタック +--- +遅延された関数呼び出しはスタックに積まれます。関数が return すると、遅延された呼び出しは後入れ先出し(LIFO)の順序で実行されます。 diff --git a/locales/ja/209-Exceptions/exceptions-1.md b/locales/ja/209-Exceptions/exceptions-1.md new file mode 100644 index 0000000..2f1f2c9 --- /dev/null +++ b/locales/ja/209-Exceptions/exceptions-1.md @@ -0,0 +1,5 @@ +### Panic +Panic は、通常の実行フローを停止する組み込み関数です。コード内で panic を呼び出すということは、その問題を呼び出し元が解決できないと判断したことを意味します。したがって、panic は、そのコードやそれを統合する人にとって、その時点で実行を続けるのが安全でない、まれなケースでのみ使用すべきです。 +以下のコード例は panic の動作を示しています: +--- +上記のように、panic が発生して処理されない場合、実行フローは停止し、すべての遅延関数が逆順で実行され、スタックトレースが出力されます。 diff --git a/locales/ja/209-Exceptions/exceptions-2.md b/locales/ja/209-Exceptions/exceptions-2.md new file mode 100644 index 0000000..33b6150 --- /dev/null +++ b/locales/ja/209-Exceptions/exceptions-2.md @@ -0,0 +1,5 @@ +### Recover +--- +エラーを戻り値として報告するには、panic 関数が呼び出されたのと同じ goroutine 内で recover 関数を呼び出し、recover 関数からエラー構造体を取得して変数に渡す必要があります: +--- +各遅延関数は、関数呼び出しの後、return 文の前に実行されます。そのため、return 文が実行される前に戻り値の変数の値を設定できます。 diff --git a/locales/ja/210-Methods/methods-1.md b/locales/ja/210-Methods/methods-1.md new file mode 100644 index 0000000..c5473a3 --- /dev/null +++ b/locales/ja/210-Methods/methods-1.md @@ -0,0 +1 @@ +XGo にはクラスがありません。しかし、型にメソッドを定義できます。メソッドは、特別なレシーバ引数を持つ関数です。レシーバは、func キーワードとメソッド名の間にある独自の引数リストに記述されます。 diff --git a/locales/ja/210-Methods/methods-2.md b/locales/ja/210-Methods/methods-2.md new file mode 100644 index 0000000..9937ce2 --- /dev/null +++ b/locales/ja/210-Methods/methods-2.md @@ -0,0 +1,3 @@ +### 非構造体型でのメソッド宣言 +--- +この例では、数値型 MyFloat に Abs メソッドがあります。レシーバの型がメソッドと同じパッケージ内で定義されている場合のみ、そのメソッドを宣言できます。他のパッケージで定義された型(int などの組み込み型を含む)にはメソッドを宣言できません。 diff --git a/locales/ja/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.md b/locales/ja/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.md new file mode 100644 index 0000000..21fe8fb --- /dev/null +++ b/locales/ja/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.md @@ -0,0 +1,11 @@ +### ポインタレシーバ +--- +ポインタレシーバを持つメソッドを宣言できます。 +--- +これは、レシーバ型がある型 T に対して *T というリテラル構文を持つことを意味します。(また、T 自体は *int のようなポインタ型であってはなりません。) +--- +例えば、ここでの Scale メソッドは *Vertex に定義されています。 +--- +ポインタレシーバを持つメソッドは、レシーバが指す値を変更できます(Scale がここで行っているように)。メソッドはしばしばレシーバを変更する必要があるため、ポインタレシーバは値レシーバよりも一般的に使われます。 +--- +値レシーバの場合、Scale メソッドは元の Vertex 値のコピーに対して操作します。(これは他の関数引数と同じ動作です。)Vertex の値を変更するには、Scale メソッドにポインタレシーバが必要です。 diff --git a/locales/ja/212-Composing-Types-by-Struct-Embedding/struct-emb-1.md b/locales/ja/212-Composing-Types-by-Struct-Embedding/struct-emb-1.md new file mode 100644 index 0000000..b5c8da7 --- /dev/null +++ b/locales/ja/212-Composing-Types-by-Struct-Embedding/struct-emb-1.md @@ -0,0 +1,9 @@ +XGo は典型的な型ベースのサブクラス化の概念を提供しませんが、構造体やインターフェースに型を埋め込むことで実装の一部を「借用」できます。インターフェースにはインターフェースだけを埋め込めます。 +--- +この例には Hello 構造体と Goodbye 構造体の 2 つの構造体型があり、それぞれが Talk インターフェースを実装しています。HelloGoodbye 構造体も Talk インターフェースを実装しており、埋め込みを使って Hello 構造体と Goodbye 構造体を 1 つの構造体にまとめています。構造体内には型だけを書き、フィールド名は付けません。 +--- +埋め込まれた要素は構造体へのポインタなので、使用する前に有効な構造体を指すように初期化する必要があります。 +--- +HelloGoodbye 構造体には forward *Forward と記述された forward メンバーもありますが、forward のメソッドを昇格させて Talk インターフェースを満たすには、hg.forward.Say() のような委譲メソッドも必要です。構造体を直接埋め込めば、この煩雑な作業を回避できます。埋め込まれた型のメソッドは自動的に利用できるようになるため、HelloGoodbye 構造体は Hello 構造体と Goodbye 構造体のメソッドも使えるようになります。 +--- +埋め込みとサブクラス化には重要な違いがあります。型を埋め込むと、その型のメソッドは外側の型のメソッドとして扱われますが、呼び出されるときのメソッドのレシーバは内側の型であり、外側の型ではありません。この例では、HelloGoodbye の Sleep メソッドが呼び出されると、委譲メソッド `helloGoodbye.Hello.Sleep()` と同じ効果があります。レシーバは `helloGoodbye.Hello` であり、`helloGoodbye` 自体ではありません。 diff --git a/locales/ja/212-Composing-Types-by-Struct-Embedding/struct-emb-2.md b/locales/ja/212-Composing-Types-by-Struct-Embedding/struct-emb-2.md new file mode 100644 index 0000000..7fc21d4 --- /dev/null +++ b/locales/ja/212-Composing-Types-by-Struct-Embedding/struct-emb-2.md @@ -0,0 +1,12 @@ +### 埋め込みはシンプルな便利手段にもなります。 +この例では、埋め込みフィールドと通常の名前付きフィールドを並べて使っています。 +--- +Job 型は *log.Logger の Print、Printf、Println などのメソッドを使えるようになります。もちろん Logger にフィールド名を付けることもできますが、その必要はありません。初期化後、Job にログを出力できます: `job.Println("starting now...")` +--- +Logger は Job 構造体の通常のフィールドなので、NewJob 関数のようにコンストラクタ内で通常どおり初期化できます。また、複合リテラルを使うこともできます: `job := &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)}` +--- +埋め込みフィールドを直接参照する必要がある場合、パッケージ修飾子を無視した型名がフィールド名として使えます。これは Job 構造体の Printf メソッドで行っているのと同じです。ここで、Job 変数 `job` の *log.Logger にアクセスする必要がある場合、`job.Logger` と書けます。Logger のメソッドを拡張したい場合に便利です。 +--- +埋め込み型は名前の衝突の問題を引き起こしますが、解決ルールはシンプルです。まず、フィールドまたはメソッド X は、型のより深くネストされた部分にある他の X を隠蔽します。log.Logger に Command というフィールドまたはメソッドがある場合、Job の Command フィールドが優先されます。 +--- +次に、同じ名前が同じネストレベルに出現する場合、通常はエラーです。Job 構造体に Logger という別のフィールドまたはメソッドがある場合、log.Logger を埋め込むのはエラーになります。ただし、重複する名前が型定義の外のプログラムで一度も参照されない場合は許容されます。この制限は、外部から埋め込まれた型の変更に対する保護を提供します。追加されたフィールドが別のサブタイプのフィールドと衝突しても、どちらのフィールドも使用されていなければ問題ありません。 diff --git a/locales/ja/213-Method-Values-and-Expressions/method-values-1.md b/locales/ja/213-Method-Values-and-Expressions/method-values-1.md new file mode 100644 index 0000000..468e4cc --- /dev/null +++ b/locales/ja/213-Method-Values-and-Expressions/method-values-1.md @@ -0,0 +1,5 @@ +### メソッド値 +--- +メソッド値を使うと、メソッドを特定のオブジェクトに束縛し、オブジェクトを暗黙的に含んだ状態で通常の関数のように呼び出せます。クロージャに似ています。例えば: +--- +式 `hello.Say` はメソッド値を作成し、`Say` メソッドを特定の変数 `hello` に束縛して、型を `func()` にします。したがって、メソッド値は関数のパラメータとして渡すことができます。 diff --git a/locales/ja/213-Method-Values-and-Expressions/method-values-2.md b/locales/ja/213-Method-Values-and-Expressions/method-values-2.md new file mode 100644 index 0000000..0274ac5 --- /dev/null +++ b/locales/ja/213-Method-Values-and-Expressions/method-values-2.md @@ -0,0 +1,5 @@ +### メソッド式 +--- +メソッド式を使うと、メソッドをレシーバを第1引数に取る関数に変換できます。例えば: +--- +したがって、Point 構造体とメソッド func (p Point) Add(another Point) を定義した場合、Point.Add と書いて func(p Point, another Point) を取得できます。 diff --git a/locales/ja/213-Method-Values-and-Expressions/method-values-3.md b/locales/ja/213-Method-Values-and-Expressions/method-values-3.md new file mode 100644 index 0000000..806c441 --- /dev/null +++ b/locales/ja/213-Method-Values-and-Expressions/method-values-3.md @@ -0,0 +1 @@ +ただし、メソッドがポインタレシーバを持つ場合、メソッド式では `(*Type).Method` と書く必要があります。例えば: diff --git a/locales/ja/214-Encapsulation/encap-1.md b/locales/ja/214-Encapsulation/encap-1.md new file mode 100644 index 0000000..5957294 --- /dev/null +++ b/locales/ja/214-Encapsulation/encap-1.md @@ -0,0 +1,5 @@ +Go はパッケージレベルでカプセル化を提供します。Go には public、private、protected キーワードはありません。可視性を制御する唯一の仕組みは、大文字と小文字を使い分けることです。 +--- +大文字の識別子はエクスポートされます。大文字はエクスポートされた識別子であることを示します。小文字の識別子はエクスポートされません。小文字はその識別子がエクスポートされず、同じパッケージ内からのみアクセスできることを示します。 +--- +エクスポートまたは非エクスポートにできる識別子は 5 種類あります。 diff --git a/locales/ja/214-Encapsulation/encap-2.md b/locales/ja/214-Encapsulation/encap-2.md new file mode 100644 index 0000000..69bbb58 --- /dev/null +++ b/locales/ja/214-Encapsulation/encap-2.md @@ -0,0 +1,3 @@ +### 1. 構造体のエクスポート +--- +ここでは、company パッケージ内で、構造体 PublicCompany はエクスポートされ、構造体 privateCompany はエクスポートされません。 diff --git a/locales/ja/214-Encapsulation/encap-3.md b/locales/ja/214-Encapsulation/encap-3.md new file mode 100644 index 0000000..d3e6bd6 --- /dev/null +++ b/locales/ja/214-Encapsulation/encap-3.md @@ -0,0 +1,3 @@ +### 2. 構造体メソッドのエクスポート +--- +Person 構造体のメソッド GetAge() はエクスポートされ、getName はエクスポートされません。 diff --git a/locales/ja/214-Encapsulation/encap-4.md b/locales/ja/214-Encapsulation/encap-4.md new file mode 100644 index 0000000..8ba5faa --- /dev/null +++ b/locales/ja/214-Encapsulation/encap-4.md @@ -0,0 +1,3 @@ +### 3. 構造体フィールドのエクスポート +--- +Student 構造体のフィールド Name はエクスポートされます。Student 構造体のフィールド age はエクスポートされません。 diff --git a/locales/ja/214-Encapsulation/encap-5.md b/locales/ja/214-Encapsulation/encap-5.md new file mode 100644 index 0000000..97eb095 --- /dev/null +++ b/locales/ja/214-Encapsulation/encap-5.md @@ -0,0 +1,3 @@ +### 4. 関数のエクスポート +--- +関数 Sum はエクスポートされ、関数 average はエクスポートされません。 diff --git a/locales/ja/214-Encapsulation/encap-6.md b/locales/ja/214-Encapsulation/encap-6.md new file mode 100644 index 0000000..284343c --- /dev/null +++ b/locales/ja/214-Encapsulation/encap-6.md @@ -0,0 +1,3 @@ +### 5. 変数のエクスポート +--- +変数 PersonName はエクスポートされ、変数 personAge はエクスポートされません。 diff --git a/locales/ja/215-Interfaces/interfaces-1.md b/locales/ja/215-Interfaces/interfaces-1.md new file mode 100644 index 0000000..2889c04 --- /dev/null +++ b/locales/ja/215-Interfaces/interfaces-1.md @@ -0,0 +1,5 @@ +まず、幾何学図形の非常にシンプルなインターフェースを定義します。面積の計算と周囲長の計算という 2 つの最も基本的なインターフェースメソッドを含みます。コードは以下の通りです: +--- +次に、2 つの構造体を定義します。矩形構造体と円形構造体です。コードは以下の通りです: +--- +そして、上記で定義したインターフェースメソッドをそれぞれ実装します。矩形構造体に関連するコードは以下の通りです: diff --git a/locales/ja/215-Interfaces/interfaces-2.md b/locales/ja/215-Interfaces/interfaces-2.md new file mode 100644 index 0000000..98d765e --- /dev/null +++ b/locales/ja/215-Interfaces/interfaces-2.md @@ -0,0 +1 @@ +次に、インターフェースの実際の使い方を見るための完全なコード例を示します: diff --git a/locales/ja/216-Interface-Satisfaction/interface-satisfy-1.md b/locales/ja/216-Interface-Satisfaction/interface-satisfy-1.md new file mode 100644 index 0000000..3e56198 --- /dev/null +++ b/locales/ja/216-Interface-Satisfaction/interface-satisfy-1.md @@ -0,0 +1,7 @@ +XGo では、インターフェースを明示的に実装する必要はありません。つまり `implement` というキーワードはありません。代わりに、インターフェースは暗黙的に満たされます。 +--- +型がインターフェースに要求されるすべてのメソッドを持っていれば、そのインターフェースを満たします。例えば、*os.File は io.Reader、io.Writer、io.Closer、io.ReadWriter を満たします。*bytes.Buffer は io.Reader、io.Writer、io.ReadWriter を満たしますが、Close メソッドがないため io.Closer は満たしません。 +--- +インターフェースの代入可能性ルールは非常にシンプルです:式の型がインターフェースを満たす場合にのみ、その式をインターフェースに代入できます。右辺自体がインターフェースの場合でも、このルールは同様に適用されます。例えば: +--- +io.ReadWriter と io.ReadWriteCloser は io.Writer のすべてのメソッドを含むため、io.ReadWriter または io.ReadWriteCloser を満たす型は必然的に io.Writer も満たします。 diff --git a/locales/ja/216-Interface-Satisfaction/interface-satisfy-2.md b/locales/ja/216-Interface-Satisfaction/interface-satisfy-2.md new file mode 100644 index 0000000..310b7ff --- /dev/null +++ b/locales/ja/216-Interface-Satisfaction/interface-satisfy-2.md @@ -0,0 +1 @@ +封筒が中の手紙を包んで隠すように、インターフェースは保持する具象型と値を包んで隠します。具象型が他のメソッドを持っていても、インターフェース型が公開するメソッドのみを呼び出せます。例えば: diff --git a/locales/ja/216-Interface-Satisfaction/interface-satisfy-3.md b/locales/ja/216-Interface-Satisfaction/interface-satisfy-3.md new file mode 100644 index 0000000..cd7069f --- /dev/null +++ b/locales/ja/216-Interface-Satisfaction/interface-satisfy-3.md @@ -0,0 +1,9 @@ +具象型は多くの無関係なインターフェースを満たすことができます。音楽、映画、書籍などのデジタル文化産物を整理・販売するプログラムを考えてみましょう。以下のような具象型のセットを定義するかもしれません: +--- + Album + Book + Movie + Magazine + Podcast + TVEpisode + Track diff --git a/locales/ja/216-Interface-Satisfaction/interface-satisfy-4.md b/locales/ja/216-Interface-Satisfaction/interface-satisfy-4.md new file mode 100644 index 0000000..5cf4b5f --- /dev/null +++ b/locales/ja/216-Interface-Satisfaction/interface-satisfy-4.md @@ -0,0 +1 @@ +関心のある各抽象をインターフェースで表せます。タイトル、作成日、作成者リスト(著者やアーティスト)など、いくつかの属性はすべてのアーティファクトに共通です。 diff --git a/locales/ja/216-Interface-Satisfaction/interface-satisfy-5.md b/locales/ja/216-Interface-Satisfaction/interface-satisfy-5.md new file mode 100644 index 0000000..b3ac4aa --- /dev/null +++ b/locales/ja/216-Interface-Satisfaction/interface-satisfy-5.md @@ -0,0 +1 @@ +他の属性は特定の種類のアーティファクトに限定されます。印刷物の属性は書籍と雑誌にのみ関連し、画面解像度を持つのは映画とテレビエピソードだけです。 diff --git a/locales/ja/216-Interface-Satisfaction/interface-satisfy-6.md b/locales/ja/216-Interface-Satisfaction/interface-satisfy-6.md new file mode 100644 index 0000000..7b9cb96 --- /dev/null +++ b/locales/ja/216-Interface-Satisfaction/interface-satisfy-6.md @@ -0,0 +1,3 @@ +これらのインターフェースは、関連する具象型をグループ化し、共通の特徴を表現する有用な方法の一つにすぎません。後で別のグループ分けを見つけるかもしれません。例えば、Audio と Video のアイテムを同じ方法で扱う必要がある場合、既存の型宣言を変更せずに、共通の特徴を表す Streamer インターフェースを定義できます。 +--- +共有する振る舞いに基づく具象型のグループ分けは、インターフェース型として表現できます。クラスベースの言語とは異なり、Go では具象型に実装させるインターフェースの一覧を明示的に宣言しません。必要に応じて新しい抽象やグループ分けを定義でき、具象型の宣言を変更する必要もありません。これは、具象型が別の作者が書いたパッケージに由来する場合に特に便利です。もちろん、具象型同士には基本的な共通性が必要です。 diff --git a/locales/ja/217-Interface-Values/interface-values-1.md b/locales/ja/217-Interface-Values/interface-values-1.md new file mode 100644 index 0000000..eab2a87 --- /dev/null +++ b/locales/ja/217-Interface-Values/interface-values-1.md @@ -0,0 +1,7 @@ +インターフェース型の値とインターフェース値は 2 つの異なる概念です。 +--- +XGo は静的型付けプログラミング言語であり、型はコンパイル時の概念であるため、型は値ではありません。型記述子の値は、名前やメソッドなど各型の情報を提供します。インターフェース値では、型コンポーネントは対応する型記述子によって表されます。 +--- +XGo では、インターフェース変数は他の型の変数と同様に、常に有効な値で初期化されます。インターフェースのゼロ値では、型コンポーネントと値コンポーネントの両方が nil に設定されます。例えば: +--- +インターフェース変数 w はゼロ値です。したがって、動的型と動的値の両方が nil です。この場合、if w == nil の結果は true です。インターフェース変数 r2 は非ゼロ値です。動的型は *bytes.Reader で、動的値は nil です。したがって r2 == nil の結果は false です。 diff --git a/locales/ja/217-Interface-Values/interface-values-2.md b/locales/ja/217-Interface-Values/interface-values-2.md new file mode 100644 index 0000000..96a9e60 --- /dev/null +++ b/locales/ja/217-Interface-Values/interface-values-2.md @@ -0,0 +1,5 @@ +インターフェース値は == と != を使って比較できます。2 つのインターフェース値が等しいのは、両方が nil の場合、または動的型が同一で動的値がその型の == の通常の振る舞いに従って等しい場合です。インターフェース値は比較可能であるため、map のキーや switch 文のオペランドとして使用できます。 +--- +ただし、2 つのインターフェース値が同じ動的型を持ち、その型が比較不可能(例えばスライス)な場合、比較は panic を引き起こします。例えば: +--- +この点で、インターフェース型は特殊です。他の型は安全に比較可能(基本型やポインタなど)か、まったく比較不可能(スライス、マップ、関数など)のいずれかですが、インターフェース値やインターフェース値を含む集約型を比較する際には、潜在的な panic のリスクに注意する必要があります。インターフェースを map のキーや switch のオペランドとして使用する場合も同様のリスクがあります。インターフェース値に比較可能な型の動的値が含まれていることが確実な場合にのみ、インターフェース値を比較してください。 diff --git a/locales/ja/217-Interface-Values/interface-values-3.md b/locales/ja/217-Interface-Values/interface-values-3.md new file mode 100644 index 0000000..c4c211e --- /dev/null +++ b/locales/ja/217-Interface-Values/interface-values-3.md @@ -0,0 +1,3 @@ +エラー処理やデバッグ時に、インターフェース値の動的型を報告すると便利なことがよくあります。そのために、fmt パッケージの %T 動詞を使用します: +--- +内部的に、fmt はリフレクションを使ってインターフェースの動的型の名前を取得しています。 diff --git a/locales/ja/218-The-error-Interface/error-1.md b/locales/ja/218-The-error-Interface/error-1.md new file mode 100644 index 0000000..090793c --- /dev/null +++ b/locales/ja/218-The-error-Interface/error-1.md @@ -0,0 +1 @@ +error インターフェースには、エラーメッセージを返す 1 つのメソッドがあります: diff --git a/locales/ja/218-The-error-Interface/error-2.md b/locales/ja/218-The-error-Interface/error-2.md new file mode 100644 index 0000000..89689dd --- /dev/null +++ b/locales/ja/218-The-error-Interface/error-2.md @@ -0,0 +1,3 @@ +エラーを作成する最もシンプルな方法は errors.New を呼び出すことです。指定されたエラーメッセージに対して新しいエラーを返します。errors パッケージ全体はわずか 4 行のコードです: +--- +errorString の基底型は文字列ではなく構造体です。これは、その表現を意図しない(または意図的な)更新から保護するためです。ポインタ型 *errorString(errorString 自体ではなく)が error インターフェースを満たす理由は、New を呼び出すたびに他のどのエラーとも等しくない異なるエラーインスタンスが割り当てられるようにするためです。 diff --git a/locales/ja/218-The-error-Interface/error-3.md b/locales/ja/218-The-error-Interface/error-3.md new file mode 100644 index 0000000..374332c --- /dev/null +++ b/locales/ja/218-The-error-Interface/error-3.md @@ -0,0 +1 @@ +io.EOF のような特別なエラーが、たまたま同じメッセージを持つエラーと等しいと比較されることは望ましくありません。 diff --git a/locales/ja/218-The-error-Interface/error-4.md b/locales/ja/218-The-error-Interface/error-4.md new file mode 100644 index 0000000..1b22578 --- /dev/null +++ b/locales/ja/218-The-error-Interface/error-4.md @@ -0,0 +1 @@ +errors.New を直接呼び出すケースは比較的少なく、文字列フォーマットも行える便利なラッパー関数 fmt.Errorf があります。 diff --git a/locales/ja/219-Type-Assertions/type-assert-1.md b/locales/ja/219-Type-Assertions/type-assert-1.md new file mode 100644 index 0000000..8bef95f --- /dev/null +++ b/locales/ja/219-Type-Assertions/type-assert-1.md @@ -0,0 +1,3 @@ +型アサーションは、インターフェース値に適用される操作です。構文的には x.(T) のように見え、x はインターフェース型の式、T は「アサートされた」型と呼ばれる型です。型アサーションは、そのオペランドの動的型がアサートされた型と一致するかどうかを検査します。 +--- +2 つの可能性があります。まず、アサートされた型 T が具象型の場合、型アサーションは x の動的型が T と同一かどうかを検査します。検査が成功すると、型アサーションの結果は x の動的値であり、その型はもちろん T です。つまり、具象型への型アサーションは、オペランドから具象値を取り出します。検査が失敗すると、操作は panic を引き起こします。例えば: diff --git a/locales/ja/219-Type-Assertions/type-assert-2.md b/locales/ja/219-Type-Assertions/type-assert-2.md new file mode 100644 index 0000000..05d4c27 --- /dev/null +++ b/locales/ja/219-Type-Assertions/type-assert-2.md @@ -0,0 +1 @@ +次に、アサートされた型 T がインターフェース型の場合、型アサーションは x の動的型が T を満たすかどうかを検査します。検査が成功しても、動的値は取り出されません。結果は同じ型と値のコンポーネントを持つインターフェース値のままですが、結果はインターフェース型 T を持ちます。つまり、インターフェース型への型アサーションは式の型を変更し、異なる(通常はより大きい)メソッドセットにアクセスできるようにしますが、インターフェース値内部の動的型と値のコンポーネントは保持されます。 diff --git a/locales/ja/219-Type-Assertions/type-assert-3.md b/locales/ja/219-Type-Assertions/type-assert-3.md new file mode 100644 index 0000000..1bbc0d8 --- /dev/null +++ b/locales/ja/219-Type-Assertions/type-assert-3.md @@ -0,0 +1 @@ +アサートされる型が何であれ、オペランドが nil インターフェース値の場合、型アサーションは失敗します。メソッド数の少ないインターフェース型への型アサーションはめったに必要なく、nil の場合を除いて代入と同様に動作します。 diff --git a/locales/ja/219-Type-Assertions/type-assert-4.md b/locales/ja/219-Type-Assertions/type-assert-4.md new file mode 100644 index 0000000..d88aec1 --- /dev/null +++ b/locales/ja/219-Type-Assertions/type-assert-4.md @@ -0,0 +1,3 @@ +多くの場合、インターフェース値の動的型が不明であり、特定の型かどうかをテストしたいことがあります。型アサーションが 2 つの結果を期待する代入に現れる場合、例えば以下の宣言のように、操作は失敗時に panic せず、代わりに成功を示すブール値の追加の第 2 結果を返します: +--- +第 2 の結果は慣例的に ok という名前の変数に代入されます。操作が失敗した場合、ok は false になり、第 1 の結果はアサートされた型のゼロ値に等しくなります。この例では nil の *bytes.Buffer です。 diff --git a/locales/ja/220-Type-Switches/type-switch.md b/locales/ja/220-Type-Switches/type-switch.md new file mode 100644 index 0000000..b77e809 --- /dev/null +++ b/locales/ja/220-Type-Switches/type-switch.md @@ -0,0 +1 @@ +型 `switch` は値ではなく型を比較します。これを使ってインターフェース値の型を判定できます。この例では、変数 `t` は対応する節の型になります。 diff --git a/locales/ko.json b/locales/ko.json new file mode 100644 index 0000000..0156183 --- /dev/null +++ b/locales/ko.json @@ -0,0 +1,60 @@ +{ + "site_title": "XGo 튜토리얼", + "breadcrumb_tutorials": "튜토리얼", + "index_heading": "튜토리얼", + "index_desc_1": "XGo는 모든 사람이 세상의 빌더가 될 수 있도록 하는 오픈소스 프로그래밍 언어입니다.", + "index_desc_2": "XGo 튜토리얼은 주석이 달린 예제 프로그램을 통해 XGo를 배우는 실습 튜토리얼입니다. 첫 번째 예제를 확인하거나 아래 전체 목록을 둘러보세요.", + "no_content_before": "아직 콘텐츠가 없습니다. ", + "no_content_link": "여기", + "no_content_after": "에서 작성에 참여해 주세요.", + "next_example": "다음 예제:", + "lang_switcher_label": "언어 전환", + "titles": { + "Sequential programming": "Sequential Programming 순차 프로그래밍", + "Structured programming": "Structured Programming 구조적 프로그래밍", + "Hello world": "Hello World", + "Values": "Values 값", + "Constants": "Constants 상수", + "Variables": "Variables 변수", + "Assignments": "Assignments 대입", + "Types": "Types 타입", + "Integers": "Integers 정수", + "Floating-Point Numbers": "Floating-Point Numbers 부동소수점", + "Complex Numbers": "Complex Numbers 복소수", + "Booleans": "Booleans 불리언", + "Strings": "Strings 문자열", + "Rational Numbers": "Rational Numbers 유리수", + "If/Else": "If/Else 조건문", + "Switch": "Switch 분기문", + "For": "For 루프", + "Arrays": "Arrays 배열", + "Slices": "Slices 슬라이스", + "Maps": "Maps 맵", + "Structs": "Structs 구조체", + "Pointers": "Pointers 포인터", + "For Each": "For Each 각 요소 순회", + "For Range": "For Range range 절", + "List Comprehension": "List Comprehension 리스트 컴프리헨션", + "Functions": "Functions 함수", + "Multiple Return Values": "Multiple Return Values 다중 반환값", + "Errors": "Errors 에러 처리", + "Function Values": "Function Values 함수 값", + "Closures": "Closures 클로저", + "Lambda expressions": "Lambda Expressions 람다식", + "Recursion": "Recursion 재귀", + "Variadic Parameters": "Variadic Parameters 가변 매개변수", + "Defer": "Defer 지연 실행", + "Exceptions": "Exceptions 예외 처리", + "Methods": "Methods 메서드", + "Methods with a Pointer Receiver": "Methods with a Pointer Receiver 포인터 리시버 메서드", + "Composing Types by Struct Embedding": "Composing Types by Struct Embedding 구조체 임베딩으로 타입 합성", + "Method Values and Expressions": "Method Values and Expressions 메서드 값과 메서드 표현식", + "Encapsulation": "Encapsulation 캡슐화", + "Interfaces": "Interfaces 인터페이스", + "Interface Satisfaction": "Interface Satisfaction 인터페이스 구현", + "Interface Values": "Interface Values 인터페이스 값", + "The error Interface": "The error Interface 에러 인터페이스", + "Type Assertions": "Type Assertions 타입 단언", + "Type Switches": "Type Switches 타입 스위치" + } +} diff --git a/locales/ko/101-Hello-world/hello-11.md b/locales/ko/101-Hello-world/hello-11.md new file mode 100644 index 0000000..2e157f1 --- /dev/null +++ b/locales/ko/101-Hello-world/hello-11.md @@ -0,0 +1,5 @@ +XGo의 Hello World에는 세 가지 작성 방법이 있습니다. +--- +### 첫 번째: 명령형 스타일 +--- +이것은 저희가 권장하는 작성 방식으로, 이해하기 매우 쉽습니다. 특히 초중학생에게 명령은 가장 이해하기 쉬운 로직이며, 함수 호출보다 훨씬 이해하기 쉽습니다. \ No newline at end of file diff --git a/locales/ko/101-Hello-world/hello-12.md b/locales/ko/101-Hello-world/hello-12.md new file mode 100644 index 0000000..f8a9ddb --- /dev/null +++ b/locales/ko/101-Hello-world/hello-12.md @@ -0,0 +1 @@ +명령형 스타일에 대한 선호를 강조하기 위해, `println`의 별칭으로 `echo`를 도입했습니다: \ No newline at end of file diff --git a/locales/ko/101-Hello-world/hello-2.md b/locales/ko/101-Hello-world/hello-2.md new file mode 100644 index 0000000..11def3a --- /dev/null +++ b/locales/ko/101-Hello-world/hello-2.md @@ -0,0 +1,3 @@ +## 두 번째: 함수 호출 스타일 +--- +이 방식은 Python과 더 유사합니다. 이 방식을 이해하려면 함수 호출이 무엇인지 이해해야 합니다. 특히 중학생이 수학 수업에서 함수(예: sin)를 배웠다면 비교적 이해하기 쉽습니다. 대부분의 프로그래밍 언어도 이러한 표준 함수 호출 구문을 지원합니다. \ No newline at end of file diff --git a/locales/ko/101-Hello-world/hello-3.md b/locales/ko/101-Hello-world/hello-3.md new file mode 100644 index 0000000..f55b1c6 --- /dev/null +++ b/locales/ko/101-Hello-world/hello-3.md @@ -0,0 +1,13 @@ +## 세 번째: 소프트웨어 엔지니어링 스타일 +--- +이것은 Go 언어에서 계승된 표준 소프트웨어 엔지니어링 방식입니다. 초보자에게는 이해하기 쉽지 않은데, 함수(func)와 패키지(package)가 무엇인지 이해해야 하기 때문입니다. 물론 장점도 있습니다 — 기능 분해와 팀 협업에 대한 기본 논리를 구축할 수 있게 해줍니다. +--- +그렇다면, XGo를 어떻게 체험할 수 있을까요? +--- +가장 간단한 방법은 XGo Playground에서 직접 체험하는 것입니다: +--- +* https://play.xgo.dev +--- +초기에 기본 문법을 학습할 때는 이 방법만으로도 충분합니다. +--- +XGo를 로컬에 설치하는 방법은 나중에 다루겠습니다. \ No newline at end of file diff --git a/locales/ko/102-Values/values.md b/locales/ko/102-Values/values.md new file mode 100644 index 0000000..e76a9b3 --- /dev/null +++ b/locales/ko/102-Values/values.md @@ -0,0 +1,9 @@ +XGo에는 문자열, 정수, 부동소수점, 불리언 등 다양한 값 타입이 있습니다. +--- +다음은 몇 가지 기본 예제입니다. +--- +문자열은 `+`를 사용하여 연결할 수 있습니다. +--- +정수와 부동소수점. +--- +불리언 값과 일반적인 불리언 연산자. \ No newline at end of file diff --git a/locales/ko/103-Constants/constants.md b/locales/ko/103-Constants/constants.md new file mode 100644 index 0000000..1fac0e0 --- /dev/null +++ b/locales/ko/103-Constants/constants.md @@ -0,0 +1,11 @@ +XGo는 문자, 문자열, 불리언 및 숫자 값의 _상수_ 를 지원합니다. +--- +`const`는 상수 값을 선언합니다. +--- +`const` 문은 `var` 문이 올 수 있는 모든 곳에서 사용할 수 있습니다. +--- +상수 표현식은 임의 정밀도의 산술 연산을 수행합니다. +--- +숫자 상수는 명시적 변환 등의 방식으로 타입이 부여되기 전까지 타입이 없습니다. +--- +숫자는 변수 대입이나 함수 호출 등 특정 타입이 필요한 컨텍스트에서 사용될 때 타입을 얻을 수 있습니다. 예를 들어, 여기서 `math.sin`은 `float64` 타입의 인자를 기대합니다. \ No newline at end of file diff --git a/locales/ko/104-Variables/vars.md b/locales/ko/104-Variables/vars.md new file mode 100644 index 0000000..00451b5 --- /dev/null +++ b/locales/ko/104-Variables/vars.md @@ -0,0 +1,11 @@ +XGo에서 _변수_ 는 명시적으로 선언되며, 컴파일러가 이를 활용하여 함수 호출의 타입 정확성 등을 검사합니다. +--- +`var`는 하나 이상의 변수를 선언합니다. +--- +여러 변수를 한 번에 선언할 수 있습니다. +--- +XGo는 초기화된 변수의 타입을 자동으로 추론합니다. +--- +초기화 없이 선언된 변수에는 _제로 값_이 할당됩니다. 예를 들어, `int`의 제로 값은 `0`입니다. +--- +`:=` 구문은 변수를 선언하고 초기화하는 축약 형태로, 이 경우 `var f string = "apple"`과 동일합니다. diff --git a/locales/ko/105-Assignments/assign-1.md b/locales/ko/105-Assignments/assign-1.md new file mode 100644 index 0000000..c678463 --- /dev/null +++ b/locales/ko/105-Assignments/assign-1.md @@ -0,0 +1,3 @@ +### 대입 +--- +`=`는 대입에 사용됩니다. 한 줄에서 여러 변수의 값을 동시에 변경할 수 있습니다. 이 방법을 통해 중간 변수 없이 값을 교환할 수 있습니다. \ No newline at end of file diff --git a/locales/ko/105-Assignments/assign-2.md b/locales/ko/105-Assignments/assign-2.md new file mode 100644 index 0000000..e9c11b6 --- /dev/null +++ b/locales/ko/105-Assignments/assign-2.md @@ -0,0 +1,3 @@ +### `=` 와 `:=` +--- +`:=`는 변수를 선언하고 초기화하는 데 사용됩니다. 한 줄에서 여러 변수를 선언하고 초기화할 수 있습니다. \ No newline at end of file diff --git a/locales/ko/106-Types/types-2.md b/locales/ko/106-Types/types-2.md new file mode 100644 index 0000000..e930605 --- /dev/null +++ b/locales/ko/106-Types/types-2.md @@ -0,0 +1,7 @@ +### XGo는 유리수에 대한 내장 지원을 제공합니다: + + +유리수를 XGo의 기본 타입으로 도입했습니다. 유리수 리터럴을 나타내기 위해 접미사 `r`을 사용합니다. 예를 들어, `1r << 200`은 2^200과 같은 값을 가진 큰 정수를 의미합니다. + +기본적으로 `1r`의 타입은 bigint입니다. +그리고 `4/5r`은 유리수 상수 4/5를 나타내며, 타입은 bigrat입니다. \ No newline at end of file diff --git a/locales/ko/106-Types/types.md b/locales/ko/106-Types/types.md new file mode 100644 index 0000000..e3af16b --- /dev/null +++ b/locales/ko/106-Types/types.md @@ -0,0 +1,19 @@ +### XGo의 기본 타입은 다음과 같습니다 +
+bool
+int8    int16   int32   int    int64    int128
+uint8   uint16  uint32  uint   uint64   uint128
+uintptr (C 언어의 size_t와 유사)
+byte (uint8의 별칭)
+rune (int32의 별칭, 유니코드 코드 포인트를 나타냄)
+string
+float32 float64
+complex64 complex128
+bigint bigrat
+unsafe.Pointer (C 언어의 void*와 유사)
+any (Go의 interface{}의 별칭)
+
+ +예제는 여러 타입의 변수를 보여주며, 변수 선언도 import 문처럼 블록으로 "묶을" 수 있음을 보여줍니다. +--- +int, uint, uintptr 타입은 32비트 시스템에서는 보통 32비트, 64비트 시스템에서는 64비트입니다. 정수 값이 필요할 때는 특정 크기나 부호 없는 정수 타입을 사용할 이유가 없다면 int를 사용해야 합니다. \ No newline at end of file diff --git a/locales/ko/107-Integers/integers.md b/locales/ko/107-Integers/integers.md new file mode 100644 index 0000000..d6ce9f3 --- /dev/null +++ b/locales/ko/107-Integers/integers.md @@ -0,0 +1,8 @@ +int 타입은 양수 또는 음수가 될 수 있는 정수를 나타냅니다. int 타입의 크기는 플랫폼에 따라 다르며, 32비트 또는 64비트입니다. int8, int16, int32, int64, int128과 같은 특정 크기의 정수 타입도 있지만, 특정 크기가 필요한 경우가 아니라면 int 타입을 사용해야 합니다. + +uint 타입은 양의 정수를 나타냅니다. uint 타입의 크기는 플랫폼에 따라 다르며, 32비트 또는 64비트입니다. uint8, uint16, uint32, uint64, uint128과 같은 특정 크기의 부호 없는 정수 타입도 있지만, 특정 크기가 필요한 경우가 아니라면 uint 타입을 사용해야 합니다. + +int 값 20은 16진수 (0x14), 8진수 (0o24), 2진수 (0b0010100)로도 표현할 수 있습니다. +uint에는 uint 리터럴이 없으며, 모든 정수 리터럴은 int 값으로 취급됩니다. + +XGo는 `_`를 숫자 구분자로 사용하는 것도 지원하며, bool을 숫자 타입으로 변환하는 것도 지원합니다. 아래 예제를 참고하세요. diff --git a/locales/ko/108-Floating-Point-Numbers/numbers.md b/locales/ko/108-Floating-Point-Numbers/numbers.md new file mode 100644 index 0000000..81dd3fb --- /dev/null +++ b/locales/ko/108-Floating-Point-Numbers/numbers.md @@ -0,0 +1,8 @@ +XGo에는 float32와 float64 두 가지 부동소수점 타입이 있습니다. +부동소수점 리터럴의 기본 타입은 float64입니다. + +부동소수점 수는 십진수 값을 정확하게 표현할 수 없습니다. 화폐나 정확한 십진수 표현이 필요한 값을 나타내는 데 사용하지 마세요! + +== 와 != 를 사용하여 부동소수점 수를 비교할 수 있지만, 그렇게 하지 않는 것이 좋습니다. 부동소수점 수의 부정확한 특성 때문에, 두 부동소수점 값이 같아야 한다고 생각할 때 같지 않을 수 있습니다. 대신, 최소 허용 오차를 정의하고 두 부동소수점 수의 차이가 그 값보다 작은지 확인하세요. 이 최소값(때로는 epsilon이라 불림)은 정밀도 요구사항에 따라 달라집니다; + +부동소수점 리터럴은 10의 거듭제곱으로도 선언할 수 있으며, 값이 0인 부동소수점 변수를 0으로 나누면 NaN(숫자가 아님)을 반환합니다. \ No newline at end of file diff --git a/locales/ko/109-Complex-Numbers/complex-1.md b/locales/ko/109-Complex-Numbers/complex-1.md new file mode 100644 index 0000000..52d6c90 --- /dev/null +++ b/locales/ko/109-Complex-Numbers/complex-1.md @@ -0,0 +1,3 @@ +### 복소수 초기화 +XGo에는 complex64와 complex128 두 가지 복소수 타입이 있습니다. +복소수를 초기화하는 것은 매우 간단합니다. 생성자 또는 초기화 구문을 사용할 수 있습니다. \ No newline at end of file diff --git a/locales/ko/109-Complex-Numbers/complex-2.md b/locales/ko/109-Complex-Numbers/complex-2.md new file mode 100644 index 0000000..0075d43 --- /dev/null +++ b/locales/ko/109-Complex-Numbers/complex-2.md @@ -0,0 +1,2 @@ +### 복소수의 구성 요소 +복소수는 실수부와 허수부 두 부분으로 구성됩니다. 이를 얻기 위해 함수를 사용합니다. \ No newline at end of file diff --git a/locales/ko/109-Complex-Numbers/complex-3.md b/locales/ko/109-Complex-Numbers/complex-3.md new file mode 100644 index 0000000..aeefbc3 --- /dev/null +++ b/locales/ko/109-Complex-Numbers/complex-3.md @@ -0,0 +1,4 @@ +### 복소수 연산 +복소수 변수는 덧셈, 뺄셈, 곱셈, 나눗셈 등 모든 연산을 수행할 수 있습니다. + +복소수에 대한 수학 연산을 수행하는 예제를 살펴봅시다. \ No newline at end of file diff --git a/locales/ko/110-Booleans/boolean-1.md b/locales/ko/110-Booleans/boolean-1.md new file mode 100644 index 0000000..e9aedea --- /dev/null +++ b/locales/ko/110-Booleans/boolean-1.md @@ -0,0 +1 @@ +bool 타입은 불리언 변수를 나타냅니다. bool 타입의 변수는 true 또는 false 두 가지 값만 가질 수 있습니다. bool의 제로 값은 false입니다. \ No newline at end of file diff --git a/locales/ko/110-Booleans/boolean-2.md b/locales/ko/110-Booleans/boolean-2.md new file mode 100644 index 0000000..9447a35 --- /dev/null +++ b/locales/ko/110-Booleans/boolean-2.md @@ -0,0 +1 @@ +XGo에서는 bool을 숫자 타입으로 변환할 수 있습니다. \ No newline at end of file diff --git a/locales/ko/111-Strings/strings.md b/locales/ko/111-Strings/strings.md new file mode 100644 index 0000000..3f7aeff --- /dev/null +++ b/locales/ko/111-Strings/strings.md @@ -0,0 +1,21 @@ +문자열은 불변의 바이트 시퀀스입니다. 문자열은 어떤 데이터든 포함할 수 있으며, 일반적으로 텍스트를 저장하는 데 사용됩니다. + +보통 큰따옴표 ""를 사용하여 문자열을 정의하지만, 특정 문자가 특별한 의미를 가진다는 점에 주의해야 합니다. 이를 이스케이프 문자라고 하며, 다음과 같습니다: + +
+  \n:줄 바꿈
+  \r:캐리지 리턴
+  \t:탭
+  \u 또는 \U:유니코드
+  \:백슬래시
+
+--- +문자열이 차지하는 바이트 길이를 알고 싶다면, XGo의 내장 함수를 사용하여 계산할 수 있습니다: +--- +문자열을 정의하는 구문은 다음과 같습니다: +--- ++ 를 사용하여 두 문자열을 연결할 수 있으며, 뒤의 문자열이 앞의 문자열 끝에 추가됩니다. +--- +여러 줄 문자열을 정의하고 싶다면, XGo도 이를 지원합니다. 기존 ""로는 줄을 넘길 수 없으며, 여러 줄 문자열을 정의하려면 백틱을 사용할 수 있습니다: ` +--- +백틱 사이의 코드는 편집기에서 특수 문자로 인식되지 않고, 문자열의 일부로만 취급됩니다. \ No newline at end of file diff --git a/locales/ko/112-Rational-Numbers/rational-1.md b/locales/ko/112-Rational-Numbers/rational-1.md new file mode 100644 index 0000000..6c6a074 --- /dev/null +++ b/locales/ko/112-Rational-Numbers/rational-1.md @@ -0,0 +1,17 @@ +XGo는 bigint, bigrat, bigfloat을 포함한 다양한 유리수 타입을 지원합니다. +다음은 bigint 타입에 대한 기본 예제입니다. +--- +### bigint 타입 변수의 선언과 대입 +--- +XGo 언어는 키워드 "var"를 사용하여 변수를 선언합니다. +--- +정수 유리수 변수는 선언 시 값을 대입할 수 있습니다. +(1r<<65)는 2의 65제곱과 같은 값입니다. +--- +참고: +값이 대입되지 않은 유리수 변수의 초기값은 0이 아니라 ``입니다. + +예상 결과: +bint1: `` +bint2: `` +bint3: `36893488147419103232` \ No newline at end of file diff --git a/locales/ko/113-If/Else/if-else.md b/locales/ko/113-If/Else/if-else.md new file mode 100644 index 0000000..ab6b76b --- /dev/null +++ b/locales/ko/113-If/Else/if-else.md @@ -0,0 +1,9 @@ +XGo에서 `if`와 `else`를 사용한 분기 처리는 매우 간단합니다. +--- +다음은 기본 예제입니다. +--- +else 없이 `if` 문을 사용할 수 있습니다. +--- +조건문 앞에 전치 문을 둘 수 있으며, 해당 문에서 선언된 변수는 모든 분기에서 사용할 수 있습니다. +--- +XGo에서는 조건식을 소괄호로 감쌀 필요가 없지만, 중괄호는 필수입니다. diff --git a/locales/ko/113-Switch/switch-1.md b/locales/ko/113-Switch/switch-1.md new file mode 100644 index 0000000..960989b --- /dev/null +++ b/locales/ko/113-Switch/switch-1.md @@ -0,0 +1,11 @@ +_Switch 문_ 은 여러 분기에 걸친 조건 판단을 표현하는 데 사용됩니다. +--- +다음은 기본적인 `switch` 예제입니다. +--- +동일한 `case` 문에서 쉼표로 여러 표현식을 구분할 수 있습니다. 이 예제에서는 선택적인 `default` 분기도 사용합니다. +--- +표현식이 없는 `switch`는 if/else 로직을 표현하는 또 다른 방법입니다. 여기서는 `case` 표현식이 상수가 아닐 수도 있음을 보여줍니다. +--- +XGo의 switch는 기본적으로 각 case 끝에서 자동으로 break됩니다. fallthrough를 사용하면 후속 case의 코드를 강제로 실행할 수 있습니다. +--- +`fallthrough`가 있는 `switch`: \ No newline at end of file diff --git a/locales/ko/114-For/for-0.md b/locales/ko/114-For/for-0.md new file mode 100644 index 0000000..8a460cb --- /dev/null +++ b/locales/ko/114-For/for-0.md @@ -0,0 +1,2 @@ +### For 루프 +XGo에는 반복 키워드가 for 하나뿐이지만, 여러 가지 형태가 있습니다. \ No newline at end of file diff --git a/locales/ko/114-For/for-1.md b/locales/ko/114-For/for-1.md new file mode 100644 index 0000000..00d4830 --- /dev/null +++ b/locales/ko/114-For/for-1.md @@ -0,0 +1,6 @@ +### 1、for/in +가장 일반적인 형태입니다. 슬라이스, 맵, 숫자 범위 또는 사용자 정의 이터레이터에 사용할 수 있습니다. + +`for value in arr/map` 형태는 슬라이스 또는 맵의 요소를 순회하는 데 사용됩니다. + +인덱스가 필요한 경우, `for index, value in arr` 형태를 사용할 수 있습니다. \ No newline at end of file diff --git a/locales/ko/114-For/for-2.md b/locales/ko/114-For/for-2.md new file mode 100644 index 0000000..157c23d --- /dev/null +++ b/locales/ko/114-For/for-2.md @@ -0,0 +1,2 @@ +### 2. Range for +for 루프에서 범위 표현식 (start:end:step)을 사용할 수 있습니다. diff --git a/locales/ko/114-For/for-3.md b/locales/ko/114-For/for-3.md new file mode 100644 index 0000000..29c2081 --- /dev/null +++ b/locales/ko/114-For/for-3.md @@ -0,0 +1,2 @@ +### 3、for/in/if +모든 for/in 형태의 루프에는 선택적 if 조건을 추가할 수 있습니다. \ No newline at end of file diff --git a/locales/ko/114-For/for-4.md b/locales/ko/114-For/for-4.md new file mode 100644 index 0000000..e2cb8fd --- /dev/null +++ b/locales/ko/114-For/for-4.md @@ -0,0 +1,2 @@ +### 4、조건 for +조건을 생략하면 무한 루프가 됩니다. break 또는 return을 사용하여 루프를 종료할 수 있습니다. \ No newline at end of file diff --git a/locales/ko/114-For/for-5.md b/locales/ko/114-For/for-5.md new file mode 100644 index 0000000..4504c70 --- /dev/null +++ b/locales/ko/114-For/for-5.md @@ -0,0 +1,2 @@ +### 5、C 스타일 for +마지막으로 전통적인 C 스타일 for 루프입니다. while 형태보다 안전한데, while을 사용할 때 카운터 업데이트를 잊어 무한 루프에 빠지기 쉽기 때문입니다. \ No newline at end of file diff --git a/locales/ko/114-For/for-6.md b/locales/ko/114-For/for-6.md new file mode 100644 index 0000000..c1decdf --- /dev/null +++ b/locales/ko/114-For/for-6.md @@ -0,0 +1,4 @@ +### break와 continue +키보드를 사용하거나 컴퓨터를 끄지 않고 무한 for 루프에서 빠져나오려면 어떻게 해야 할까요? 이것이 break 문의 역할입니다. 다른 언어의 break 문처럼 루프를 즉시 종료합니다. 물론 무한 for 문뿐만 아니라 모든 for 문에서 break를 사용할 수 있습니다. + +XGo에는 continue 키워드도 있으며, for 루프 본문의 나머지를 건너뛰고 다음 반복으로 바로 진행합니다. 기술적으로 continue 문이 반드시 필요한 것은 아닙니다. \ No newline at end of file diff --git a/locales/ko/114-For/for-7.md b/locales/ko/114-For/for-7.md new file mode 100644 index 0000000..3606650 --- /dev/null +++ b/locales/ko/114-For/for-7.md @@ -0,0 +1,2 @@ +### "for" 문에 레이블 붙이기 +기본적으로 break와 continue 키워드는 직접 포함하고 있는 for 루프에 작용합니다. 중첩된 for 루프가 있을 때 외부 루프의 반복을 종료하거나 건너뛰고 싶다면 어떻게 할까요? 예제를 살펴봅시다. 문자열 순회 프로그램을 수정하여 문자 "l"을 만나면 즉시 해당 문자열의 순회를 중단하도록 하겠습니다. \ No newline at end of file diff --git a/locales/ko/116-Arrays/arrays1.md b/locales/ko/116-Arrays/arrays1.md new file mode 100644 index 0000000..e6e70a8 --- /dev/null +++ b/locales/ko/116-Arrays/arrays1.md @@ -0,0 +1,13 @@ +XGo에서 _배열_ 은 특정 길이를 가진 동일 타입 요소들의 번호가 매겨진 시퀀스입니다. +--- +### 1차원 배열의 선언 +--- +여기서 정확히 5개의 `int`를 담을 수 있는 배열 `a`를 생성합니다. 요소 타입과 길이 모두 배열 타입의 일부입니다. 기본적으로 배열은 제로 값으로 초기화되며, `int`의 경우 `0`입니다. +--- +`array[index] = value` 구문을 사용하여 특정 인덱스에 값을 설정하고, `array[index]`로 값을 가져올 수 있습니다. +--- +내장 함수 `len`은 배열의 길이를 반환합니다. +--- +이 구문을 사용하면 한 줄에서 배열을 선언하고 초기화할 수 있습니다. +--- +배열의 길이를 직접 작성하고 싶지 않다면, 이 방법을 사용하여 컴파일러가 배열의 길이를 자동으로 계산하도록 할 수 있습니다. \ No newline at end of file diff --git a/locales/ko/116-Arrays/arrays2.md b/locales/ko/116-Arrays/arrays2.md new file mode 100644 index 0000000..f8ae00c --- /dev/null +++ b/locales/ko/116-Arrays/arrays2.md @@ -0,0 +1,5 @@ +### 다차원 배열의 선언 +--- +배열 타입은 1차원이지만, 타입을 조합하여 다차원 데이터 구조를 만들 수 있습니다. +--- +더 많은 차원이 필요한 경우 직접 확장할 수 있습니다. 예를 들어 2*2*3 의 3차원 배열을 선언할 수 있습니다 \ No newline at end of file diff --git a/locales/ko/116-Arrays/arrays3.md b/locales/ko/116-Arrays/arrays3.md new file mode 100644 index 0000000..fcd5ca4 --- /dev/null +++ b/locales/ko/116-Arrays/arrays3.md @@ -0,0 +1,7 @@ +#### 팁 +--- +XGo에서 배열의 내용을 선언하지 않으면, 컴파일러가 자동으로 배열을 0으로 초기화합니다; bool 타입 배열의 경우 초기값은 false입니다. +--- +더 많은 배열 값 타입에 대해, XGo는 문자열, 정수, 부동소수점, 불리언 등을 지원합니다. 자세한 내용은 이 장을 참조하세요: +--- +* https://tutorial.xgo.dev/values \ No newline at end of file diff --git a/locales/ko/117-Slices/slices-01.md b/locales/ko/117-Slices/slices-01.md new file mode 100644 index 0000000..0266a8e --- /dev/null +++ b/locales/ko/117-Slices/slices-01.md @@ -0,0 +1,4 @@ +### XGo 스타일의 슬라이스 리터럴 +슬라이스는 동일 타입 데이터 요소의 컬렉션입니다. 슬라이스 리터럴은 대괄호로 둘러싸인 표현식 목록입니다. 인덱스 표현식을 사용하여 개별 요소에 접근할 수 있습니다. 인덱스는 0부터 시작합니다. + +XGo에서는 len 메서드를 통해 슬라이스 길이를 직접 얻을 수 있으며, 슬라이스 리터럴에 대한 타입 변환도 가능합니다: \ No newline at end of file diff --git a/locales/ko/117-Slices/slices-02.md b/locales/ko/117-Slices/slices-02.md new file mode 100644 index 0000000..130de70 --- /dev/null +++ b/locales/ko/117-Slices/slices-02.md @@ -0,0 +1,6 @@ +### 슬라이스 +배열은 고정된 크기를 가집니다. 반면 슬라이스는 배열 요소에 대한 동적 크기의 유연한 뷰입니다. 실제로 슬라이스가 배열보다 훨씬 더 자주 사용됩니다. + +타입 []T는 요소 타입이 T인 슬라이스입니다. + +슬라이스는 콜론으로 구분된 두 개의 인덱스(하한과 상한)를 지정하여 형성됩니다: a[low : high]. 이것은 첫 번째 요소는 포함하지만 마지막 요소는 포함하지 않는 반개 구간을 선택합니다. \ No newline at end of file diff --git a/locales/ko/117-Slices/slices-03.md b/locales/ko/117-Slices/slices-03.md new file mode 100644 index 0000000..ea8ac25 --- /dev/null +++ b/locales/ko/117-Slices/slices-03.md @@ -0,0 +1,6 @@ +### 슬라이스는 배열의 참조와 유사합니다 +슬라이스는 어떤 데이터도 저장하지 않으며, 단지 기반 배열의 한 구간을 설명할 뿐입니다. + +슬라이스의 요소를 변경하면 기반 배열의 해당 요소가 수정됩니다. + +동일한 기반 배열을 공유하는 다른 슬라이스들도 이러한 변경 사항을 볼 수 있습니다. \ No newline at end of file diff --git a/locales/ko/117-Slices/slices-04.md b/locales/ko/117-Slices/slices-04.md new file mode 100644 index 0000000..6b4ddc1 --- /dev/null +++ b/locales/ko/117-Slices/slices-04.md @@ -0,0 +1,8 @@ +### 슬라이스 리터럴 +슬라이스 리터럴은 길이가 없는 배열 리터럴과 유사합니다. +
+이것은 배열 리터럴입니다:
+[3]bool{true, true, false}
+아래 표현은 위와 동일한 배열을 생성한 후, 이를 참조하는 슬라이스를 만듭니다:
+[]bool{true, true, false}
+
\ No newline at end of file diff --git a/locales/ko/117-Slices/slices-05.md b/locales/ko/117-Slices/slices-05.md new file mode 100644 index 0000000..b39c306 --- /dev/null +++ b/locales/ko/117-Slices/slices-05.md @@ -0,0 +1,13 @@ +### 슬라이스의 기본값 +슬라이싱할 때 상한 또는 하한을 생략하여 기본값을 사용할 수 있습니다. + +하한의 기본값은 0이고, 상한의 기본값은 슬라이스의 길이입니다. +
+배열의 경우
+var a [10]int
+다음 슬라이스 표현식들은 동일합니다:
+a[0:10]
+a[:10]
+a[0:]
+a[:]
+
\ No newline at end of file diff --git a/locales/ko/117-Slices/slices-06.md b/locales/ko/117-Slices/slices-06.md new file mode 100644 index 0000000..a7adcb8 --- /dev/null +++ b/locales/ko/117-Slices/slices-06.md @@ -0,0 +1,10 @@ +### 슬라이스의 길이와 용량 +슬라이스는 길이와 용량을 모두 가집니다. + +슬라이스의 길이는 포함하고 있는 요소의 수입니다. + +슬라이스의 용량은 슬라이스의 첫 번째 요소부터 기반 배열 끝까지의 요소 수입니다. + +슬라이스 s의 길이와 용량은 len(s)와 cap(s) 표현식으로 얻을 수 있습니다. + +재슬라이싱을 통해 슬라이스의 길이를 확장할 수 있으며, 충분한 용량이 있어야 합니다. 예제 프로그램의 슬라이스 작업 중 하나를 용량을 초과하도록 수정하여 어떤 일이 일어나는지 확인해 보세요. \ No newline at end of file diff --git a/locales/ko/117-Slices/slices-07.md b/locales/ko/117-Slices/slices-07.md new file mode 100644 index 0000000..46b90cb --- /dev/null +++ b/locales/ko/117-Slices/slices-07.md @@ -0,0 +1,4 @@ +### nil 슬라이스 +슬라이스의 제로 값은 nil입니다. + +nil 슬라이스는 길이와 용량이 모두 0이며, 기반 배열이 없습니다. \ No newline at end of file diff --git a/locales/ko/117-Slices/slices-08.md b/locales/ko/117-Slices/slices-08.md new file mode 100644 index 0000000..661c1e2 --- /dev/null +++ b/locales/ko/117-Slices/slices-08.md @@ -0,0 +1,12 @@ +### make로 슬라이스 생성하기 +슬라이스는 내장 make 함수로 생성할 수 있습니다; 이것이 동적 크기 배열을 생성하는 방법입니다. + +make 함수는 제로 값 배열을 할당하고, 해당 배열을 참조하는 슬라이스를 반환합니다: + +
+a := make([]int, 5)			// len(a)=5
+용량을 지정하려면 make에 세 번째 인자를 전달합니다:
+b := make([]int, 0, 5) 		// len(b)=0, cap(b)=5
+b = b[:cap(b)] 				// len(b)=5, cap(b)=5
+b = b[1:]      				// len(b)=4, cap(b)=4
+
\ No newline at end of file diff --git a/locales/ko/117-Slices/slices-09.md b/locales/ko/117-Slices/slices-09.md new file mode 100644 index 0000000..4a045ec --- /dev/null +++ b/locales/ko/117-Slices/slices-09.md @@ -0,0 +1,3 @@ +### 슬라이스의 슬라이스 +슬라이스는 다른 슬라이스를 포함하여 어떤 타입이든 포함할 수 있습니다. +틱택토 게임판을 만들어 봅시다. \ No newline at end of file diff --git a/locales/ko/117-Slices/slices-10.md b/locales/ko/117-Slices/slices-10.md new file mode 100644 index 0000000..e6473f4 --- /dev/null +++ b/locales/ko/117-Slices/slices-10.md @@ -0,0 +1,10 @@ +### 슬라이스에 요소 추가하기 +슬라이스에 새 요소를 추가하는 것은 매우 일반적인 작업이므로, XGo는 내장 append 함수를 제공합니다. 내장 패키지 문서에서 append의 사용법을 설명합니다. + +func append(s []T, vs ...T) []T + +append의 첫 번째 매개변수 s는 타입 T의 슬라이스이며, 나머지 매개변수는 슬라이스에 추가할 T 타입의 값입니다. + +append의 반환값은 원래 슬라이스의 모든 요소와 새로 제공된 값을 포함하는 슬라이스입니다. + +s의 기반 배열이 너무 작아 주어진 모든 값을 담을 수 없으면, 더 큰 배열이 할당됩니다. 반환된 슬라이스는 새로 할당된 배열을 가리킵니다. \ No newline at end of file diff --git a/locales/ko/117-Slices/slices-11.md b/locales/ko/117-Slices/slices-11.md new file mode 100644 index 0000000..d3cde07 --- /dev/null +++ b/locales/ko/117-Slices/slices-11.md @@ -0,0 +1,4 @@ +### Range +for 루프의 range 형태는 슬라이스 또는 map을 순회할 수 있습니다. + +슬라이스를 순회할 때, 각 반복에서 두 개의 값이 반환됩니다. 첫 번째는 인덱스이고, 두 번째는 해당 인덱스에 있는 요소의 복사본입니다. \ No newline at end of file diff --git a/locales/ko/117-Slices/slices-12.md b/locales/ko/117-Slices/slices-12.md new file mode 100644 index 0000000..b2976c3 --- /dev/null +++ b/locales/ko/117-Slices/slices-12.md @@ -0,0 +1,9 @@ +### Range 계속 + +
+_ 에 대입하여 인덱스 또는 값을 건너뛸 수 있습니다.
+for i, _ := range pow
+for _, value := range pow
+인덱스만 필요한 경우, 두 번째 변수를 생략할 수 있습니다.
+for i := range pow
+
\ No newline at end of file diff --git a/locales/ko/118-Maps/map-0.md b/locales/ko/118-Maps/map-0.md new file mode 100644 index 0000000..b4ca516 --- /dev/null +++ b/locales/ko/118-Maps/map-0.md @@ -0,0 +1,17 @@ +_Maps_ 는 XGo에 내장된 [연관 데이터 타입](http://en.wikipedia.org/wiki/Associative_array)입니다(다른 언어에서는 _해시_ 또는 _딕셔너리_ 라고 부르기도 합니다). +--- +### Map 기초 +--- +`make(map[key-type]val-type)`를 사용하여 빈 map을 생성합니다. +--- +일반적인 `name[key] = val` 구문으로 키/값 쌍을 설정합니다. +--- +`println` 등으로 map을 출력하면, 모든 키/값 쌍이 표시됩니다. +--- +`name[key]`로 특정 키에 대한 값을 가져옵니다. +--- +내장 함수 `len`은 map에 사용하면 키/값 쌍의 수를 반환합니다. +--- +내장 함수 `delete`는 map에서 키/값 쌍을 삭제합니다. +--- +map에서 값을 가져올 때 선택적인 두 번째 반환값은 해당 키가 map에 존재하는지 나타냅니다. 이는 누락된 키와 제로 값 키(`0`이나 `""`)를 구별하는 데 사용할 수 있습니다. 여기서는 값 자체가 필요 없으므로 _빈 식별자_ `_`로 무시했습니다. \ No newline at end of file diff --git a/locales/ko/118-Maps/map-1.md b/locales/ko/118-Maps/map-1.md new file mode 100644 index 0000000..8dd8f3f --- /dev/null +++ b/locales/ko/118-Maps/map-1.md @@ -0,0 +1,11 @@ +### XGo 스타일의 Map 리터럴 +--- +map[string]int +--- +map[string]float64 +--- +map[string]interface{} +--- +map[int]interface{} +--- +빈 map의 타입은 map[string]interface{}입니다 \ No newline at end of file diff --git a/locales/ko/118-Maps/map-2.md b/locales/ko/118-Maps/map-2.md new file mode 100644 index 0000000..03686b9 --- /dev/null +++ b/locales/ko/118-Maps/map-2.md @@ -0,0 +1,13 @@ +### Go 스타일의 Map 리터럴 +--- +다음 구문을 사용하여 한 줄에서 새 map을 선언하고 초기화할 수도 있습니다. +--- +{"Hello": 1, "xsw": 3} +--- +{"Hello": 1, "xsw": 3.4} +--- +{"Hello": 1, "xsw": "XGo"} +--- +{1: 1.4, 3: "XGo"} +--- +{} \ No newline at end of file diff --git a/locales/ko/119-Structs/struct-1.md b/locales/ko/119-Structs/struct-1.md new file mode 100644 index 0000000..5b0d4f6 --- /dev/null +++ b/locales/ko/119-Structs/struct-1.md @@ -0,0 +1,9 @@ +Map은 특정 유형의 데이터를 저장하는 편리한 방법이지만, 한계가 있습니다. Map은 특정 키만 허용하도록 제한할 수 없기 때문에 API를 정의할 수 없습니다. Map의 모든 값도 동일한 타입이어야 합니다. 이러한 이유로 map은 함수 간에 데이터를 전달하는 이상적인 방법이 아닙니다. 함께 그룹화하고 싶은 관련 데이터가 있을 때는 구조체를 정의해야 합니다. + +구조체 타입은 키워드 type, 구조체 타입 이름, 키워드 struct, 그리고 중괄호({}) 쌍으로 정의합니다. 중괄호 안에 구조체의 필드를 나열합니다. var 선언에서 변수 이름을 먼저 쓰고 변수 타입을 나중에 쓰는 것처럼, 구조체 필드도 필드 이름을 먼저 쓰고 필드 타입을 나중에 씁니다. + +또한 map 리터럴과 달리 구조체 선언에서 필드 사이에는 쉼표가 없습니다. 함수 내부 또는 외부에서 구조체 타입을 정의할 수 있습니다. 함수 내부에서 정의된 구조체 타입은 해당 함수 내에서만 사용할 수 있습니다. 기술적으로 모든 블록 수준에서 구조체를 정의할 수 있습니다. + +구조체 타입이 선언되면, 해당 타입의 변수를 정의할 수 있습니다. +--- +여기서는 var 선언을 사용합니다. fred에 값이 할당되지 않았으므로, person 구조체 타입의 제로 값을 얻습니다. 제로 값 구조체의 모든 필드는 해당 필드의 제로 값으로 설정됩니다. \ No newline at end of file diff --git a/locales/ko/119-Structs/struct-2.md b/locales/ko/119-Structs/struct-2.md new file mode 100644 index 0000000..8b8a056 --- /dev/null +++ b/locales/ko/119-Structs/struct-2.md @@ -0,0 +1,4 @@ +### 비어있지 않은 구조체 리터럴에는 두 가지 다른 스타일이 있습니다. +구조체 리터럴은 중괄호 안에 쉼표로 구분된 필드 값 목록으로 지정할 수 있습니다: +--- +이 구조체 리터럴 형식을 사용할 때는 구조체의 모든 필드에 대해 값을 지정해야 하며, 값은 구조체 정의에서 선언된 순서대로 필드에 할당됩니다. \ No newline at end of file diff --git a/locales/ko/119-Structs/struct-3.md b/locales/ko/119-Structs/struct-3.md new file mode 100644 index 0000000..13e0815 --- /dev/null +++ b/locales/ko/119-Structs/struct-3.md @@ -0,0 +1,4 @@ + +두 번째 구조체 리터럴 스타일은 map 리터럴 스타일과 유사합니다: +--- +구조체의 필드 이름을 사용하여 값을 지정할 수 있습니다. 이 스타일을 사용하면 일부 키를 생략하고 필드를 임의의 순서로 지정할 수 있습니다. 지정되지 않은 필드는 제로 값으로 설정됩니다. 두 구조체 리터럴 스타일을 혼합해서 사용할 수 없습니다. 모든 필드에 키 이름을 지정하거나 아예 지정하지 않아야 합니다. 모든 필드가 항상 지정되는 작은 구조체의 경우, 더 간단한 구조체 리터럴 스타일을 사용하면 됩니다. 그 외의 경우에는 키 이름 방식을 사용하세요. 더 장황하지만, 구조체 정의를 참조하지 않고도 어떤 값이 어떤 필드에 할당되는지 명확하게 보여줍니다. 또한 유지보수가 더 쉽습니다. 필드 이름 없이 구조체를 초기화한 상태에서 향후 구조체에 추가 필드가 추가되면 코드가 컴파일되지 않습니다. \ No newline at end of file diff --git a/locales/ko/119-Structs/struct-4.md b/locales/ko/119-Structs/struct-4.md new file mode 100644 index 0000000..795ca67 --- /dev/null +++ b/locales/ko/119-Structs/struct-4.md @@ -0,0 +1,4 @@ + +구조체의 필드는 점 표기법을 사용하여 접근합니다: +--- +map을 읽고 쓸 때 대괄호를 사용하는 것처럼, 구조체 필드를 읽고 쓸 때는 점 표기법을 사용합니다. \ No newline at end of file diff --git a/locales/ko/119-Structs/struct-5.md b/locales/ko/119-Structs/struct-5.md new file mode 100644 index 0000000..839aeb2 --- /dev/null +++ b/locales/ko/119-Structs/struct-5.md @@ -0,0 +1,6 @@ +### 익명 구조체 +구조체 타입에 먼저 이름을 부여하지 않고 변수가 구조체 타입을 구현하도록 선언할 수도 있습니다. 이를 익명 구조체라고 합니다. +--- +이 예제에서 변수 person과 pet의 타입은 익명 구조체입니다. 이름이 있는 구조체 타입에서와 마찬가지로 익명 구조체의 필드를 대입하고 읽을 수 있습니다. 이름이 있는 구조체의 인스턴스를 구조체 리터럴로 초기화할 수 있듯이, 익명 구조체에서도 마찬가지입니다. 단일 인스턴스에만 연결된 데이터 타입이 언제 유용한지 궁금할 수 있습니다. 익명 구조체가 유용한 두 가지 일반적인 상황이 있습니다. 첫 번째는 외부 데이터를 구조체로 변환하거나 구조체를 외부 데이터(JSON 또는 protocol buffers 등)로 변환할 때입니다. 이를 데이터의 직렬화 및 역직렬화라고 합니다. + +테스트를 작성하는 것은 익명 구조체의 또 다른 일반적인 사용 사례입니다. \ No newline at end of file diff --git a/locales/ko/119-Structs/struct-6.md b/locales/ko/119-Structs/struct-6.md new file mode 100644 index 0000000..32f6fb1 --- /dev/null +++ b/locales/ko/119-Structs/struct-6.md @@ -0,0 +1,9 @@ +### 구조체의 비교와 변환 +구조체의 비교 가능 여부는 필드에 따라 달라집니다. 비교 가능한 타입으로만 구성된 구조체는 비교할 수 있으며, 슬라이스나 map 필드를 포함하는 구조체는 비교할 수 없습니다(이후 장에서 보겠지만, 함수와 채널 필드도 구조체를 비교 불가능하게 만듭니다). + +Python이나 Ruby와 달리, == 와 != 가 비교 불가능한 구조체에서 작동하도록 동등성을 재정의할 수 있는 매직 메서드는 없습니다. 물론 구조체를 비교하기 위한 자체 함수를 작성할 수는 있습니다. + +XGo가 서로 다른 기본 타입의 변수 간 비교를 허용하지 않는 것처럼, 서로 다른 타입의 구조체를 나타내는 변수 간 비교도 허용하지 않습니다. 두 구조체의 필드가 동일한 이름, 순서, 타입을 가지면 한 구조체 타입에서 다른 구조체 타입으로 타입 변환을 수행할 수 있습니다. 이것이 무엇을 의미하는지 살펴봅시다. 다음 구조체가 주어졌을 때: +--- +secondPerson의 인스턴스를 firstPerson으로 타입 변환할 수 있지만, 필드의 순서가 다르기 때문에 thirdPerson의 인스턴스를 firstPerson으로 변환할 수는 없습니다. +그러나 firstPerson의 인스턴스와 secondPerson 또는 thirdPerson의 인스턴스를 == 로 비교할 수는 없습니다. 서로 다른 타입이기 때문입니다. \ No newline at end of file diff --git a/locales/ko/119-Structs/struct-7.md b/locales/ko/119-Structs/struct-7.md new file mode 100644 index 0000000..3529ecc --- /dev/null +++ b/locales/ko/119-Structs/struct-7.md @@ -0,0 +1 @@ +익명 구조체에는 이에 대한 작은 특이점이 있습니다: 비교 중인 두 구조체 변수 중 적어도 하나가 익명 구조체 타입인 경우, 두 구조체의 필드가 동일한 이름, 순서, 타입을 가지면 타입 변환 없이 비교할 수 있습니다. 마찬가지로, 두 구조체의 필드가 동일한 이름, 순서, 타입을 가지면 이름이 있는 구조체 타입과 익명 구조체 타입 간에 대입할 수도 있습니다. \ No newline at end of file diff --git a/locales/ko/120-Pointers/pointer-1.md b/locales/ko/120-Pointers/pointer-1.md new file mode 100644 index 0000000..faf6a47 --- /dev/null +++ b/locales/ko/120-Pointers/pointer-1.md @@ -0,0 +1,5 @@ +포인터는 값이 메모리 주소인 변수입니다. 포인터는 주소 연산자로 알려진 앰퍼샌드(& 문자)를 사용하여 정의하며, 뒤에 변수 이름이 옵니다. + +포인터의 타입은 고정되어 있습니다. 즉, int에 대한 포인터를 만들면 가리키는 값을 변경할 수 있지만, float64와 같은 다른 타입을 저장하는 메모리 주소를 가리키는 데 사용할 수 없습니다. 이 제한은 중요하며, 포인터는 단순한 메모리 주소가 아니라 특정 타입의 값을 저장할 수 있는 메모리 주소입니다. + +포인터의 타입은 생성 시 사용된 변수 타입을 기반으로 하며, 앞에 별표(* 문자)가 붙습니다. second라는 변수의 타입은 *int인데, first 변수에 주소 연산자를 적용하여 생성되었고, first의 값이 int 타입이기 때문입니다. *int 타입을 보면, int 변수를 저장하는 메모리 주소가 값인 변수라는 것을 알 수 있습니다. \ No newline at end of file diff --git a/locales/ko/120-Pointers/pointer-2.md b/locales/ko/120-Pointers/pointer-2.md new file mode 100644 index 0000000..1dd5380 --- /dev/null +++ b/locales/ko/120-Pointers/pointer-2.md @@ -0,0 +1,6 @@ +### 포인터 따라가기 +"포인터를 따라간다"는 말은 포인터가 참조하는 메모리 주소에 있는 값을 읽는 것을 의미하며, 별표(* 문자)를 사용하여 수행합니다. 별표는 XGo에게 포인터를 따라가서 해당 메모리 위치의 값을 가져오도록 지시합니다. 이를 포인터 역참조라고 합니다. +--- +첫 번째 새 문은 새 변수를 정의하며, 변수 타입이 *int, 즉 int 값에 대한 포인터임을 강조하기 위해 var 키워드를 사용했습니다. 다음 문은 second 변수의 값을 새 변수에 대입하며, 이는 second와 myNewPointer의 값이 모두 first 값의 메모리 위치임을 의미합니다. 어느 포인터를 따라가든 동일한 메모리 위치에 접근하므로, myNewPointer를 증가시키면 second 포인터를 따라가서 얻은 값에도 영향을 미칩니다. + +일반적인 오해는 first와 second 변수가 동일한 값을 가진다는 것이지만, 실제로는 그렇지 않습니다. 두 개의 값이 있습니다. 하나는 first라는 변수를 통해 접근할 수 있는 int 값입니다. 또한 first 값의 메모리 위치를 저장하는 *int 값도 있습니다. *int 값을 따라가면 저장된 int 값에 접근할 수 있습니다. 하지만 *int 값 자체도 값이기 때문에, 다른 변수에 대입하거나, 함수 호출의 인수로 사용하는 등 독립적으로 사용할 수 있습니다. \ No newline at end of file diff --git a/locales/ko/120-Pointers/pointer-3.md b/locales/ko/120-Pointers/pointer-3.md new file mode 100644 index 0000000..d8014ef --- /dev/null +++ b/locales/ko/120-Pointers/pointer-3.md @@ -0,0 +1,6 @@ +### 포인터의 제로 값 이해하기 +정의되었지만 값이 할당되지 않은 포인터의 제로 값은 nil입니다. + +포인터 second는 정의되었지만 값이 초기화되지 않았으며, println 함수를 사용하여 출력됩니다. 그런 다음 주소 연산자를 사용하여 first 변수에 대한 포인터를 생성하고, second의 값을 다시 출력합니다. + +값이 할당되지 않은 포인터를 따라가면 런타임 오류가 발생합니다. \ No newline at end of file diff --git a/locales/ko/120-Pointers/pointer-4.md b/locales/ko/120-Pointers/pointer-4.md new file mode 100644 index 0000000..94fb418 --- /dev/null +++ b/locales/ko/120-Pointers/pointer-4.md @@ -0,0 +1,4 @@ +### 포인터에 대한 포인터 +포인터는 메모리 위치를 저장하므로, 다른 포인터의 메모리 주소를 값으로 가지는 포인터를 만들 수 있습니다. +--- +포인터 체인을 따라가는 구문은 다소 번거로울 수 있습니다. 이 경우 두 개의 별표가 필요합니다. 첫 번째 별표는 포인터를 따라가 메모리 위치에서 second라는 변수가 저장한 값, 즉 *int 값을 가져옵니다. 두 번째 별표는 second라는 포인터를 따라가, first 변수가 저장한 값의 메모리 위치에 접근합니다. 대부분의 프로젝트에서 이런 작업이 필요하지는 않지만, 포인터가 어떻게 작동하는지와 체인을 따라 데이터 값을 가져오는 방법을 잘 확인시켜줍니다. \ No newline at end of file diff --git a/locales/ko/121-For-Each/for-each-1.md b/locales/ko/121-For-Each/for-each-1.md new file mode 100644 index 0000000..4bb6daa --- /dev/null +++ b/locales/ko/121-For-Each/for-each-1.md @@ -0,0 +1 @@ +XGo에는 foreach 루프가 없지만, for 루프를 "foreach"처럼 사용할 수 있습니다. `range`라는 키워드가 있으며, for와 range를 함께 쓰면 루프에서 키 또는 값을 선택할 수 있습니다. diff --git a/locales/ko/121-For-Each/for-each-2.md b/locales/ko/121-For-Each/for-each-2.md new file mode 100644 index 0000000..9de5143 --- /dev/null +++ b/locales/ko/121-For-Each/for-each-2.md @@ -0,0 +1,6 @@ +### for-range 루프 +for-range 루프의 흥미로운 점은 두 개의 루프 변수를 얻는다는 것입니다. 첫 번째 변수는 순회 대상 데이터 구조에서의 위치이고, 두 번째는 해당 위치의 값입니다. 두 루프 변수의 관용적인 이름은 순회 대상에 따라 달라집니다. 배열, 슬라이스 또는 문자열을 순회할 때는 일반적으로 인덱스를 나타내는 `i`를 사용합니다. map을 순회할 때는 `k`(key의 약자)를 사용합니다. + +두 번째 변수는 보통 값을 나타내는 `v`로 이름 짓지만, 순회되는 값의 타입에 따라 이름을 짓기도 합니다. + +키에 접근할 필요가 없으면 변수 이름으로 밑줄(`_`)을 사용합니다. 이는 XGo에게 해당 값을 무시하라고 지시합니다. diff --git a/locales/ko/121-For-Each/for-each-3.md b/locales/ko/121-For-Each/for-each-3.md new file mode 100644 index 0000000..db3eb4e --- /dev/null +++ b/locales/ko/121-For-Each/for-each-3.md @@ -0,0 +1,2 @@ +### Map 순회하기 +for-range로 map을 순회할 때에는 몇 가지 흥미로운 점이 있습니다. diff --git a/locales/ko/121-For-Range/range.md b/locales/ko/121-For-Range/range.md new file mode 100644 index 0000000..042c152 --- /dev/null +++ b/locales/ko/121-For-Range/range.md @@ -0,0 +1,11 @@ +`range`는 다양한 데이터 구조의 요소를 순회할 수 있습니다. 이미 배운 데이터 구조들과 함께 `range`를 사용하는 방법을 살펴봅시다. +--- +여기서는 `range`를 사용하여 슬라이스의 숫자를 합산합니다. 배열도 이와 같은 방식으로 사용할 수 있습니다. +--- +배열과 슬라이스에서 `range`를 사용하면 각 요소의 인덱스와 값을 모두 제공합니다. 위에서는 인덱스가 필요 없어 빈 식별자 `_`로 무시했습니다. 하지만 때로는 인덱스가 실제로 필요합니다. +--- +map에서 `range`를 사용하면 키/값 쌍을 순회합니다. +--- +`range`는 map의 키만 순회할 수도 있습니다. +--- +문자열에서 `range`를 사용하면 유니코드 코드 포인트를 순회합니다. 첫 번째 값은 `rune`의 시작 바이트 인덱스이고, 두 번째 값은 `rune` 자체입니다. diff --git a/locales/ko/121-List-Comprehension/listcompr.md b/locales/ko/121-List-Comprehension/listcompr.md new file mode 100644 index 0000000..ce69454 --- /dev/null +++ b/locales/ko/121-List-Comprehension/listcompr.md @@ -0,0 +1,9 @@ +홀수와 짝수 목록 생성하기. +--- +제곱수 +--- +반복 +--- +문자열 분할 +--- +지정된 문자로 문자열 분할 \ No newline at end of file diff --git a/locales/ko/201-Functions/funcs.md b/locales/ko/201-Functions/funcs.md new file mode 100644 index 0000000..dcbaf4a --- /dev/null +++ b/locales/ko/201-Functions/funcs.md @@ -0,0 +1,9 @@ +_함수_ 는 XGo의 핵심입니다. 몇 가지 다른 예제를 통해 함수에 대해 배워보겠습니다. +--- +이 함수는 두 개의 `int` 매개변수를 받아 그 합을 `int`로 반환합니다. +--- +XGo는 명시적 반환을 요구합니다. 즉, 마지막 표현식의 값을 자동으로 반환하지 않습니다. +--- +연속된 여러 매개변수가 동일한 타입을 가질 경우, 마지막 매개변수에만 타입을 선언하고 앞의 동일 타입 매개변수에서는 타입명을 생략할 수 있습니다. +--- +함수 호출은 예상대로 `name(args)` 형식으로 합니다. diff --git a/locales/ko/202-Multiple-Return-Values/multi-rets.md b/locales/ko/202-Multiple-Return-Values/multi-rets.md new file mode 100644 index 0000000..12c7317 --- /dev/null +++ b/locales/ko/202-Multiple-Return-Values/multi-rets.md @@ -0,0 +1,7 @@ +XGo는 _다중 반환값_ 을 기본적으로 지원합니다. 이 기능은 관용적인 XGo 코드에서 자주 사용되며, 예를 들어 함수에서 결과값과 에러값을 동시에 반환할 때 활용됩니다. +--- +이 함수 시그니처에서 `(int, int)`는 함수가 두 개의 `int`를 반환함을 나타냅니다. +--- +여기서는 _다중 대입_ 을 사용하여 호출에서 반환된 두 개의 서로 다른 값을 사용합니다. +--- +반환값의 일부만 필요한 경우 빈 식별자 `_`를 사용할 수 있습니다. diff --git a/locales/ko/203-Errors/errors.md b/locales/ko/203-Errors/errors.md new file mode 100644 index 0000000..4699411 --- /dev/null +++ b/locales/ko/203-Errors/errors.md @@ -0,0 +1,15 @@ +XGo에서는 명시적이고 별도의 반환값을 통해 에러를 전달하는 것이 관용적인 방식입니다. 이는 Java나 Ruby 같은 언어에서 사용하는 예외 방식과 다르며, C 언어에서 때때로 사용되는 단일 결과/에러 값 오버로딩 방식과도 다릅니다. XGo의 방식을 사용하면 어떤 함수가 에러를 반환하는지 쉽게 파악할 수 있고, 다른 비에러 작업에 사용하는 것과 동일한 언어 구조로 에러를 처리할 수 있습니다. +--- +관례적으로 에러는 마지막 반환값이며, 내장 인터페이스인 `error` 타입을 가집니다. +--- +`errors.New`는 주어진 에러 메시지로 기본적인 `error` 값을 생성합니다. +--- +에러 위치의 `nil` 값은 에러가 없음을 나타냅니다. +--- +사용자 정의 타입에 `Error()` 메서드를 구현하여 `error`로 사용할 수 있습니다. 다음은 위 예제의 변형으로, 사용자 정의 타입을 사용하여 인자 에러를 명시적으로 표현합니다. +--- +이 예제에서는 `&argError` 구문을 사용하여 새로운 구조체를 생성하고, `arg`와 `prob` 두 필드에 값을 제공합니다. +--- +아래의 두 루프는 에러를 반환하는 각 함수를 테스트합니다. `if` 줄에서 인라인 에러 검사를 사용하는 것은 XGo 코드에서 흔히 사용되는 관용적 패턴입니다. +--- +사용자 정의 에러의 데이터를 프로그래밍 방식으로 사용하려면 타입 단언을 통해 에러를 사용자 정의 에러 타입의 인스턴스로 가져와야 합니다. diff --git a/locales/ko/204-Function Values/func-values.md b/locales/ko/204-Function Values/func-values.md new file mode 100644 index 0000000..7a3f88a --- /dev/null +++ b/locales/ko/204-Function Values/func-values.md @@ -0,0 +1 @@ +함수도 값입니다. 다른 값처럼 전달할 수 있습니다. 함수 값은 함수의 인자와 반환값으로 사용할 수 있습니다. diff --git a/locales/ko/205-Closures/closures.md b/locales/ko/205-Closures/closures.md new file mode 100644 index 0000000..aafb972 --- /dev/null +++ b/locales/ko/205-Closures/closures.md @@ -0,0 +1 @@ +XGo 함수는 클로저가 될 수 있습니다. 클로저는 함수 본문 외부의 변수를 참조하는 함수 값입니다. 이 함수는 참조된 변수에 접근하고 값을 할당할 수 있습니다. 이런 의미에서 함수는 해당 변수에 "바인딩"됩니다. 예를 들어, adder 함수는 클로저를 반환합니다. 각 클로저는 자신만의 sum 변수에 바인딩됩니다. diff --git a/locales/ko/205-Lambda-expressions/lambda-expressions-1.md b/locales/ko/205-Lambda-expressions/lambda-expressions-1.md new file mode 100644 index 0000000..26220d7 --- /dev/null +++ b/locales/ko/205-Lambda-expressions/lambda-expressions-1.md @@ -0,0 +1,3 @@ +람다식은 이름을 지정하지 않고 함수를 인라인으로 정의할 때 사용됩니다. +--- +다음 예제는 XGo 스타일의 람다식을 보여주며, 더 간결하고 이해하기 쉽습니다. diff --git a/locales/ko/205-Lambda-expressions/lambda-expressions-2.md b/locales/ko/205-Lambda-expressions/lambda-expressions-2.md new file mode 100644 index 0000000..af16e1d --- /dev/null +++ b/locales/ko/205-Lambda-expressions/lambda-expressions-2.md @@ -0,0 +1 @@ +람다를 정의하되 즉시 실행하지 않으려면 식별자만 생략하면 됩니다. 예를 들어: diff --git a/locales/ko/205-Lambda-expressions/lambda-expressions-3.md b/locales/ko/205-Lambda-expressions/lambda-expressions-3.md new file mode 100644 index 0000000..ac256b8 --- /dev/null +++ b/locales/ko/205-Lambda-expressions/lambda-expressions-3.md @@ -0,0 +1 @@ +람다를 정의하고 즉시 실행하려면 식별자를 생략하고, 함수 본문의 닫는 중괄호 뒤에 괄호로 감싼 인자 목록을 추가하면 됩니다. 예를 들어: diff --git a/locales/ko/206-Recursion/recursion.md b/locales/ko/206-Recursion/recursion.md new file mode 100644 index 0000000..9d2d943 --- /dev/null +++ b/locales/ko/206-Recursion/recursion.md @@ -0,0 +1,6 @@ +XGo 프로그래밍 언어는 재귀를 지원합니다. 즉, 함수가 자기 자신을 호출할 수 있습니다. 하지만 재귀를 사용할 때 프로그래머는 함수의 종료 조건을 정의하는 데 주의해야 하며, 그렇지 않으면 무한 루프에 빠지게 됩니다. +재귀 함수는 팩토리얼 계산, 피보나치 수열 생성 등 많은 수학적 문제를 해결하는 데 매우 유용합니다. +--- +이 예제는 재귀 함수를 사용하여 주어진 숫자의 팩토리얼을 계산합니다. +--- +이 예제는 재귀 함수를 사용하여 주어진 숫자의 피보나치 수열을 생성하는 방법을 보여줍니다. diff --git a/locales/ko/207-Variadic-Parameters/variadic.md b/locales/ko/207-Variadic-Parameters/variadic.md new file mode 100644 index 0000000..7e2026f --- /dev/null +++ b/locales/ko/207-Variadic-Parameters/variadic.md @@ -0,0 +1 @@ +가변 개수의 인자로 호출되는 joinstr 함수를 가변 인자 함수라고 합니다. joinstr 함수의 선언에서 마지막 매개변수의 타입 앞에 줄임표, 즉 (…)가 붙어 있습니다. 이는 함수가 임의의 개수의 string 인자를 받을 수 있음을 나타냅니다. joinstr(elements...) 호출 시 ...를 붙이지 않으면 타입이 맞지 않아 컴파일되지 않습니다. elements는 string 타입이 아니기 때문입니다. diff --git a/locales/ko/208-Defer/defer-1.md b/locales/ko/208-Defer/defer-1.md new file mode 100644 index 0000000..a534617 --- /dev/null +++ b/locales/ko/208-Defer/defer-1.md @@ -0,0 +1,3 @@ +defer 문은 둘러싸는 함수가 반환될 때까지 함수의 실행을 지연시킵니다. 지연된 호출의 인자는 즉시 평가되지만, 함수 호출은 둘러싸는 함수가 반환될 때까지 실행되지 않습니다. Defer는 다양한 정리 작업을 수행하는 함수를 단순화하는 데 주로 사용됩니다. +--- +defer 문을 사용하면 파일을 연 직후에 닫는 것을 고려할 수 있어, 함수 내에 return 문이 몇 개 있든 상관없이 파일이 반드시 닫히도록 보장할 수 있습니다. diff --git a/locales/ko/208-Defer/defer-2.md b/locales/ko/208-Defer/defer-2.md new file mode 100644 index 0000000..1f687c7 --- /dev/null +++ b/locales/ko/208-Defer/defer-2.md @@ -0,0 +1,3 @@ +### defer 스택 +--- +지연된 함수 호출은 스택에 쌓입니다. 함수가 반환될 때, 지연된 호출은 후입선출(LIFO) 순서로 실행됩니다. diff --git a/locales/ko/209-Exceptions/exceptions-1.md b/locales/ko/209-Exceptions/exceptions-1.md new file mode 100644 index 0000000..9384d27 --- /dev/null +++ b/locales/ko/209-Exceptions/exceptions-1.md @@ -0,0 +1,5 @@ +### Panic +Panic은 정상적인 실행 흐름을 중단시키는 내장 함수입니다. 코드에서 panic을 호출한다는 것은 해당 문제가 호출자에 의해 해결될 수 없다고 판단했음을 의미합니다. 따라서 그 시점에 실행을 계속하는 것이 안전하지 않은 극히 드문 경우에만 panic을 사용해야 합니다. +다음 코드 예제는 panic의 작동 방식을 보여줍니다: +--- +위에서 보듯이, panic이 발생하고 처리되지 않으면 실행 흐름이 중단되고, 모든 지연된 함수가 역순으로 실행되며, 스택 트레이스가 출력됩니다. diff --git a/locales/ko/209-Exceptions/exceptions-2.md b/locales/ko/209-Exceptions/exceptions-2.md new file mode 100644 index 0000000..0286b8e --- /dev/null +++ b/locales/ko/209-Exceptions/exceptions-2.md @@ -0,0 +1,5 @@ +### Recover +--- +에러를 반환값으로 보고하려면, panic 함수가 호출된 동일한 고루틴에서 recover 함수를 호출하고, recover 함수에서 에러 구조체를 가져와 변수에 전달해야 합니다: +--- +각 지연된 함수는 함수 호출 후 return 문 전에 실행됩니다. 따라서 return 문이 실행되기 전에 반환 변수의 값을 설정할 수 있습니다. diff --git a/locales/ko/210-Methods/methods-1.md b/locales/ko/210-Methods/methods-1.md new file mode 100644 index 0000000..790053f --- /dev/null +++ b/locales/ko/210-Methods/methods-1.md @@ -0,0 +1 @@ +XGo에는 클래스가 없습니다. 하지만 타입에 메서드를 정의할 수 있습니다. 메서드는 특별한 리시버 인자를 가진 함수입니다. 리시버는 func 키워드와 메서드 이름 사이의 자체 인자 목록에 나타납니다. diff --git a/locales/ko/210-Methods/methods-2.md b/locales/ko/210-Methods/methods-2.md new file mode 100644 index 0000000..09ec601 --- /dev/null +++ b/locales/ko/210-Methods/methods-2.md @@ -0,0 +1,3 @@ +### 비구조체 타입에 메서드 선언 +--- +이 예제에서 숫자 타입 MyFloat에 Abs 메서드가 있는 것을 볼 수 있습니다. 메서드와 동일한 패키지에 정의된 타입에만 리시버가 있는 메서드를 선언할 수 있습니다. 다른 패키지에 정의된 타입(int 등 내장 타입 포함)에는 메서드를 선언할 수 없습니다. diff --git a/locales/ko/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.md b/locales/ko/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.md new file mode 100644 index 0000000..cc79e95 --- /dev/null +++ b/locales/ko/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.md @@ -0,0 +1,11 @@ +### 포인터 리시버 +--- +포인터 리시버를 가진 메서드를 선언할 수 있습니다. +--- +이는 리시버 타입이 특정 타입 T에 대해 *T라는 리터럴 구문을 가진다는 것을 의미합니다. (또한, T 자체는 *int와 같은 포인터 타입일 수 없습니다.) +--- +예를 들어, 여기서 Scale 메서드는 *Vertex에 정의되어 있습니다. +--- +포인터 리시버를 가진 메서드는 리시버가 가리키는 값을 수정할 수 있습니다(Scale이 하는 것처럼). 메서드는 리시버를 수정해야 하는 경우가 많기 때문에, 포인터 리시버가 값 리시버보다 더 자주 사용됩니다. +--- +값 리시버를 사용하면 Scale 메서드는 원래 Vertex 값의 복사본에 대해 동작합니다. (이는 다른 함수 인자의 동작과 동일합니다.) Vertex 값을 수정하려면 Scale 메서드에 포인터 리시버를 사용해야 합니다. diff --git a/locales/ko/212-Composing-Types-by-Struct-Embedding/struct-emb-1.md b/locales/ko/212-Composing-Types-by-Struct-Embedding/struct-emb-1.md new file mode 100644 index 0000000..53e0e47 --- /dev/null +++ b/locales/ko/212-Composing-Types-by-Struct-Embedding/struct-emb-1.md @@ -0,0 +1,9 @@ +XGo는 전형적인 타입 기반 서브클래싱 개념을 제공하지 않지만, 구조체나 인터페이스에 타입을 임베딩하여 구현의 일부를 "빌려올" 수 있습니다. 인터페이스에는 인터페이스만 임베딩할 수 있습니다. +--- +예제에는 Hello 구조체와 Goodbye 구조체라는 두 개의 구조체 타입이 있으며, 각각 Talk 인터페이스를 구현합니다. HelloGoodbye 구조체도 Talk 인터페이스를 구현하며, 임베딩을 사용하여 Hello 구조체와 Goodbye 구조체를 하나의 구조체로 결합합니다. 구조체 안에는 타입만 나열하고 필드 이름은 붙이지 않습니다. +--- +임베딩된 요소는 구조체 포인터이므로, 사용하기 전에 유효한 구조체를 가리키도록 초기화해야 합니다. +--- +HelloGoodbye 구조체에는 `forward *Forward`로 작성된 forward 멤버도 있지만, forward의 메서드를 승격시키고 Talk 인터페이스를 만족시키려면 위임 메서드도 제공해야 합니다. 예: `hg.forward.Say()`. 구조체를 직접 임베딩하면 이런 번거로운 작업을 피할 수 있습니다. 임베딩된 타입의 메서드는 자동으로 사용할 수 있게 되므로, HelloGoodbye 구조체도 Hello 구조체와 Goodbye 구조체의 메서드를 갖게 됩니다. +--- +임베딩과 서브클래싱 사이에는 중요한 차이점이 있습니다. 타입을 임베딩하면 해당 타입의 메서드가 외부 타입의 메서드처럼 취급되지만, 호출될 때 메서드의 리시버는 외부 타입이 아니라 내부 타입입니다. 이 예제에서 HelloGoodbye의 Sleep 메서드가 호출되면, 위임 메서드 `helloGoodbye.Hello.Sleep()`과 같은 효과를 가집니다. 리시버는 `helloGoodbye` 자체가 아니라 `helloGoodbye.Hello`입니다. diff --git a/locales/ko/212-Composing-Types-by-Struct-Embedding/struct-emb-2.md b/locales/ko/212-Composing-Types-by-Struct-Embedding/struct-emb-2.md new file mode 100644 index 0000000..80a0629 --- /dev/null +++ b/locales/ko/212-Composing-Types-by-Struct-Embedding/struct-emb-2.md @@ -0,0 +1,12 @@ +### 임베딩은 단순한 편의 수단이 될 수도 있습니다. +이 예제는 임베딩된 필드와 일반적인 이름이 있는 필드를 함께 사용하는 것을 보여줍니다. +--- +Job 타입은 이제 *log.Logger의 Print, Printf, Println 등의 메서드를 사용할 수 있습니다. 물론 Logger에 필드 이름을 줄 수도 있지만 그럴 필요는 없습니다. 초기화 후에는 Job에 로그를 출력할 수 있습니다: `job.Println("starting now...")` +--- +Logger는 Job 구조체의 일반 필드이므로, NewJob 함수처럼 생성자 안에서 일반적인 방식으로 초기화할 수 있고, 복합 리터럴도 사용할 수 있습니다: `job := &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)}` +--- +임베딩된 필드를 직접 참조해야 하는 경우, 패키지 한정자를 무시한 타입 이름이 필드 이름으로 사용됩니다. 이는 Job 구조체의 Printf 메서드에서와 같습니다. 여기서 Job 변수 `job`의 *log.Logger에 접근해야 한다면 `job.Logger`로 작성하면 되며, Logger의 메서드를 확장하고 싶을 때 유용합니다. +--- +임베딩 타입은 이름 충돌 문제를 야기하지만, 해결 규칙은 간단합니다. 첫째, 필드나 메서드 X는 타입의 더 깊이 중첩된 부분에 있는 다른 항목 X를 숨깁니다. log.Logger에 Command라는 필드나 메서드가 있다면 Job의 Command 필드가 우선됩니다. +--- +둘째, 같은 이름이 같은 중첩 수준에 나타나면 일반적으로 오류입니다. Job 구조체에 Logger라는 다른 필드나 메서드가 있다면 log.Logger를 임베딩하는 것은 잘못된 것입니다. 하지만 중복된 이름이 타입 정의 외부의 프로그램에서 한 번도 언급되지 않는다면 허용됩니다. 이 한정은 외부에서 임베딩된 타입의 변경으로부터 일정한 보호를 제공합니다. 추가된 필드가 다른 하위 타입의 필드와 충돌하더라도 두 필드 모두 사용되지 않았다면 문제가 없습니다. diff --git a/locales/ko/213-Method-Values-and-Expressions/method-values-1.md b/locales/ko/213-Method-Values-and-Expressions/method-values-1.md new file mode 100644 index 0000000..f257e39 --- /dev/null +++ b/locales/ko/213-Method-Values-and-Expressions/method-values-1.md @@ -0,0 +1,5 @@ +### 메서드 값 +--- +메서드 값을 사용하면 메서드를 특정 객체에 바인딩한 다음, 객체가 암시적으로 포함된 상태에서 일반 함수처럼 호출할 수 있으며, 이는 클로저와 유사합니다. 예를 들어: +--- +표현식 `hello.Say`는 `Say` 메서드를 특정 변수 `hello`에 바인딩하는 메서드 값을 생성하여, `func()` 타입을 갖게 합니다. 따라서 메서드 값은 함수의 매개변수로 전달할 수 있습니다. diff --git a/locales/ko/213-Method-Values-and-Expressions/method-values-2.md b/locales/ko/213-Method-Values-and-Expressions/method-values-2.md new file mode 100644 index 0000000..8cbda0c --- /dev/null +++ b/locales/ko/213-Method-Values-and-Expressions/method-values-2.md @@ -0,0 +1,5 @@ +### 메서드 표현식 +--- +메서드 표현식은 메서드를 리시버를 첫 번째 인자로 받는 함수로 변환합니다. 예를 들어: +--- +따라서 Point 구조체와 메서드 func (p Point) Add(another Point)를 정의했다면, Point.Add를 작성하여 func(p Point, another Point)를 얻을 수 있습니다. diff --git a/locales/ko/213-Method-Values-and-Expressions/method-values-3.md b/locales/ko/213-Method-Values-and-Expressions/method-values-3.md new file mode 100644 index 0000000..01603dd --- /dev/null +++ b/locales/ko/213-Method-Values-and-Expressions/method-values-3.md @@ -0,0 +1 @@ +하지만 메서드가 포인터 리시버를 가진 경우, 메서드 표현식에서는 `(*Type).Method`로 작성해야 합니다. 예를 들어: diff --git a/locales/ko/214-Encapsulation/encap-1.md b/locales/ko/214-Encapsulation/encap-1.md new file mode 100644 index 0000000..092322e --- /dev/null +++ b/locales/ko/214-Encapsulation/encap-1.md @@ -0,0 +1,5 @@ +Go는 패키지 수준에서 캡슐화를 제공합니다. Go에는 public, private, protected 키워드가 없습니다. 가시성을 제어하는 유일한 방법은 대문자와 소문자를 구분해 사용하는 것입니다. +--- +대문자 식별자는 내보내집니다. 대문자는 이것이 내보내진 식별자임을 나타냅니다. 소문자 식별자는 내보내지지 않습니다. 소문자는 해당 식별자가 내보내지지 않으며 동일한 패키지 내에서만 접근할 수 있음을 나타냅니다. +--- +내보내기 또는 비내보내기가 가능한 다섯 종류의 식별자가 있습니다. diff --git a/locales/ko/214-Encapsulation/encap-2.md b/locales/ko/214-Encapsulation/encap-2.md new file mode 100644 index 0000000..3a507ee --- /dev/null +++ b/locales/ko/214-Encapsulation/encap-2.md @@ -0,0 +1,3 @@ +### 1. 구조체 내보내기 +--- +여기서 company 패키지에서 구조체 PublicCompany는 내보내지고, 구조체 privateCompany는 내보내지지 않습니다. diff --git a/locales/ko/214-Encapsulation/encap-3.md b/locales/ko/214-Encapsulation/encap-3.md new file mode 100644 index 0000000..510b804 --- /dev/null +++ b/locales/ko/214-Encapsulation/encap-3.md @@ -0,0 +1,3 @@ +### 2. 구조체 메서드 내보내기 +--- +Person 구조체의 메서드 GetAge()는 내보내지고, getName은 내보내지지 않습니다. diff --git a/locales/ko/214-Encapsulation/encap-4.md b/locales/ko/214-Encapsulation/encap-4.md new file mode 100644 index 0000000..1125aa5 --- /dev/null +++ b/locales/ko/214-Encapsulation/encap-4.md @@ -0,0 +1,3 @@ +### 3. 구조체 필드 내보내기 +--- +Student 구조체의 필드 Name은 내보내집니다. Student 구조체의 필드 age는 내보내지지 않습니다. diff --git a/locales/ko/214-Encapsulation/encap-5.md b/locales/ko/214-Encapsulation/encap-5.md new file mode 100644 index 0000000..7be2f7a --- /dev/null +++ b/locales/ko/214-Encapsulation/encap-5.md @@ -0,0 +1,3 @@ +### 4. 함수 내보내기 +--- +함수 Sum은 내보내지고, 함수 average는 내보내지지 않습니다. diff --git a/locales/ko/214-Encapsulation/encap-6.md b/locales/ko/214-Encapsulation/encap-6.md new file mode 100644 index 0000000..1c7c2f6 --- /dev/null +++ b/locales/ko/214-Encapsulation/encap-6.md @@ -0,0 +1,3 @@ +### 5. 변수 내보내기 +--- +변수 PersonName은 내보내지고, 변수 personAge는 내보내지지 않습니다. diff --git a/locales/ko/215-Interfaces/interfaces-1.md b/locales/ko/215-Interfaces/interfaces-1.md new file mode 100644 index 0000000..72e068f --- /dev/null +++ b/locales/ko/215-Interfaces/interfaces-1.md @@ -0,0 +1,5 @@ +먼저, 두 가지 가장 기본적인 인터페이스 메서드(면적 계산과 둘레 계산)를 포함하는 매우 간단한 기하학 인터페이스를 정의합니다. 코드는 다음과 같습니다: +--- +다음으로, 직사각형 구조체와 원형 구조체 두 가지를 정의합니다. 코드는 다음과 같습니다: +--- +그런 다음 위에서 정의한 인터페이스 메서드를 각각 구현합니다. 직사각형 구조체와 관련된 코드는 다음과 같습니다: diff --git a/locales/ko/215-Interfaces/interfaces-2.md b/locales/ko/215-Interfaces/interfaces-2.md new file mode 100644 index 0000000..e261719 --- /dev/null +++ b/locales/ko/215-Interfaces/interfaces-2.md @@ -0,0 +1 @@ +이제 인터페이스의 실제 사용법을 보여주는 완전한 코드 예제를 살펴보겠습니다: diff --git a/locales/ko/216-Interface-Satisfaction/interface-satisfy-1.md b/locales/ko/216-Interface-Satisfaction/interface-satisfy-1.md new file mode 100644 index 0000000..f765b57 --- /dev/null +++ b/locales/ko/216-Interface-Satisfaction/interface-satisfy-1.md @@ -0,0 +1,7 @@ +XGo에서 인터페이스는 명시적으로 구현할 필요가 없습니다. 즉, `implement` 키워드는 없습니다. 대신 인터페이스는 암시적으로 만족됩니다. +--- +타입이 인터페이스가 요구하는 모든 메서드를 보유하면 해당 인터페이스를 만족합니다. 예를 들어, *os.File은 io.Reader, io.Writer, io.Closer, io.ReadWriter를 만족합니다. *bytes.Buffer는 io.Reader, io.Writer, io.ReadWriter를 만족하지만, Close 메서드가 없으므로 io.Closer는 만족하지 않습니다. +--- +인터페이스의 할당 규칙은 매우 간단합니다: 표현식의 타입이 인터페이스를 만족하는 경우에만 해당 인터페이스에 할당할 수 있습니다. 이 규칙은 우변이 인터페이스인 경우에도 동일하게 적용됩니다. 예를 들어: +--- +io.ReadWriter와 io.ReadWriteCloser는 io.Writer의 모든 메서드를 포함하므로, io.ReadWriter나 io.ReadWriteCloser를 만족하는 모든 타입은 반드시 io.Writer도 만족합니다. diff --git a/locales/ko/216-Interface-Satisfaction/interface-satisfy-2.md b/locales/ko/216-Interface-Satisfaction/interface-satisfy-2.md new file mode 100644 index 0000000..d9f9cdb --- /dev/null +++ b/locales/ko/216-Interface-Satisfaction/interface-satisfy-2.md @@ -0,0 +1 @@ +봉투가 담고 있는 편지를 감싸고 숨기듯이, 인터페이스는 자신이 보유한 구체적 타입과 값을 감싸고 숨깁니다. 구체적 타입에 다른 메서드가 있더라도 인터페이스 타입이 노출하는 메서드만 호출할 수 있습니다. 예를 들어: diff --git a/locales/ko/216-Interface-Satisfaction/interface-satisfy-3.md b/locales/ko/216-Interface-Satisfaction/interface-satisfy-3.md new file mode 100644 index 0000000..2528096 --- /dev/null +++ b/locales/ko/216-Interface-Satisfaction/interface-satisfy-3.md @@ -0,0 +1,9 @@ +하나의 구체적 타입은 서로 관련 없는 많은 인터페이스를 만족할 수 있습니다. 음악, 영화, 책과 같은 디지털 문화 상품을 관리하거나 판매하는 프로그램을 생각해 봅시다. 다음과 같은 구체적 타입 집합을 정의할 수 있습니다: +--- + Album + Book + Movie + Magazine + Podcast + TVEpisode + Track diff --git a/locales/ko/216-Interface-Satisfaction/interface-satisfy-4.md b/locales/ko/216-Interface-Satisfaction/interface-satisfy-4.md new file mode 100644 index 0000000..a15fa97 --- /dev/null +++ b/locales/ko/216-Interface-Satisfaction/interface-satisfy-4.md @@ -0,0 +1 @@ +관심 있는 각 추상화를 인터페이스로 표현할 수 있습니다. 제목, 생성 날짜, 제작자 목록(저자 또는 아티스트)과 같은 일부 속성은 모든 상품에 공통적입니다. diff --git a/locales/ko/216-Interface-Satisfaction/interface-satisfy-5.md b/locales/ko/216-Interface-Satisfaction/interface-satisfy-5.md new file mode 100644 index 0000000..49ad059 --- /dev/null +++ b/locales/ko/216-Interface-Satisfaction/interface-satisfy-5.md @@ -0,0 +1 @@ +다른 속성은 특정 유형의 상품에만 해당됩니다. 인쇄물의 속성은 책과 잡지에만 관련되며, 화면 해상도는 영화와 TV 에피소드에만 해당됩니다. diff --git a/locales/ko/216-Interface-Satisfaction/interface-satisfy-6.md b/locales/ko/216-Interface-Satisfaction/interface-satisfy-6.md new file mode 100644 index 0000000..550362d --- /dev/null +++ b/locales/ko/216-Interface-Satisfaction/interface-satisfy-6.md @@ -0,0 +1,3 @@ +이러한 인터페이스는 관련된 구체적 타입을 함께 묶고 공통된 측면을 표현하는 유용한 방법 중 하나일 뿐입니다. 나중에 다른 그룹화 방법을 발견할 수도 있습니다. 예를 들어 Audio와 Video 항목을 동일한 방식으로 처리해야 한다면, 기존 타입 선언을 변경하지 않고 공통 측면을 나타내는 Streamer 인터페이스를 정의할 수 있습니다. +--- +공유된 동작에 기반한 구체적 타입의 그룹화는 인터페이스 타입으로 표현할 수 있습니다. 클래스 기반 언어와 달리, Go에서는 구체적 타입이 구현해야 할 인터페이스 목록을 명시적으로 선언하지 않습니다. 대신 필요할 때 새로운 추상화나 관심 있는 그룹을 정의할 수 있으며, 구체적 타입의 선언을 수정할 필요가 없습니다. 이는 구체적 타입이 다른 작성자가 작성한 패키지에서 온 경우 특히 유용합니다. 물론, 구체적 타입 사이에는 근본적인 공통성이 있어야 합니다. diff --git a/locales/ko/217-Interface-Values/interface-values-1.md b/locales/ko/217-Interface-Values/interface-values-1.md new file mode 100644 index 0000000..4aca408 --- /dev/null +++ b/locales/ko/217-Interface-Values/interface-values-1.md @@ -0,0 +1,7 @@ +인터페이스 타입의 값과 인터페이스 값은 서로 다른 개념입니다. +--- +XGo는 정적 타입 프로그래밍 언어이며, 타입은 컴파일 시점의 개념이므로 타입은 값이 아닙니다. 타입 디스크립터의 값은 이름과 메서드 등 각 타입에 대한 정보를 제공합니다. 인터페이스 값에서 타입 컴포넌트는 해당 타입 디스크립터로 표현됩니다. +--- +XGo에서 인터페이스 변수는 다른 타입 변수와 마찬가지로 항상 유효한 값으로 초기화됩니다. 인터페이스의 제로 값은 타입과 값 컴포넌트 모두 nil로 설정됩니다. 예를 들어: +--- +인터페이스 변수 w는 제로 값입니다. 따라서 동적 타입과 동적 값이 모두 nil입니다. 이 경우 if w == nil의 결과는 true입니다. 인터페이스 변수 r2는 비제로 값입니다. 동적 타입은 *bytes.Reader이고 동적 값은 nil입니다. 따라서 r2 == nil의 결과는 false입니다. diff --git a/locales/ko/217-Interface-Values/interface-values-2.md b/locales/ko/217-Interface-Values/interface-values-2.md new file mode 100644 index 0000000..efa7fa3 --- /dev/null +++ b/locales/ko/217-Interface-Values/interface-values-2.md @@ -0,0 +1,5 @@ +인터페이스 값은 ==와 !=를 사용하여 비교할 수 있습니다. 두 인터페이스 값이 같으려면 둘 다 nil이거나, 동적 타입이 동일하고 동적 값이 해당 타입의 ==의 일반적인 동작에 따라 같아야 합니다. 인터페이스 값은 비교 가능하므로, map의 키나 switch 문의 피연산자로 사용할 수 있습니다. +--- +그러나 비교하는 두 인터페이스 값의 동적 타입이 같지만 해당 타입이 비교 불가능한 경우(예: 슬라이스), 비교 시 panic이 발생합니다. 예를 들어: +--- +이런 점에서 인터페이스 타입은 특별합니다. 다른 타입은 안전하게 비교 가능하거나(기본 타입, 포인터 등) 전혀 비교할 수 없지만(슬라이스, 맵, 함수 등), 인터페이스 값이나 인터페이스 값을 포함하는 집합 타입을 비교할 때는 잠재적인 panic 위험에 주의해야 합니다. 인터페이스를 map 키나 switch 피연산자로 사용할 때도 유사한 위험이 있습니다. 인터페이스 값에 비교 가능한 타입의 동적 값이 포함되어 있다고 확신할 때만 인터페이스 값을 비교해야 합니다. diff --git a/locales/ko/217-Interface-Values/interface-values-3.md b/locales/ko/217-Interface-Values/interface-values-3.md new file mode 100644 index 0000000..2b74a2c --- /dev/null +++ b/locales/ko/217-Interface-Values/interface-values-3.md @@ -0,0 +1,3 @@ +에러 처리 시나 디버깅 중에 인터페이스 값의 동적 타입을 보고하는 것이 종종 유용합니다. 이를 위해 fmt 패키지의 %T 동사를 사용합니다: +--- +내부적으로 fmt는 리플렉션을 사용하여 인터페이스 동적 타입의 이름을 얻습니다. diff --git a/locales/ko/218-The-error-Interface/error-1.md b/locales/ko/218-The-error-Interface/error-1.md new file mode 100644 index 0000000..8827eb4 --- /dev/null +++ b/locales/ko/218-The-error-Interface/error-1.md @@ -0,0 +1 @@ +error 인터페이스는 에러 메시지를 반환하는 하나의 메서드를 가집니다: diff --git a/locales/ko/218-The-error-Interface/error-2.md b/locales/ko/218-The-error-Interface/error-2.md new file mode 100644 index 0000000..94ddc0e --- /dev/null +++ b/locales/ko/218-The-error-Interface/error-2.md @@ -0,0 +1,3 @@ +에러를 생성하는 가장 간단한 방법은 errors.New를 호출하는 것으로, 주어진 에러 메시지에 대한 새로운 에러를 반환합니다. 전체 errors 패키지는 단 네 줄의 코드입니다: +--- +errorString의 기본 타입이 문자열이 아닌 구조체인 이유는, 의도하지 않은(또는 의도적인) 수정으로부터 표현을 보호하기 위해서입니다. 포인터 타입 *errorString(errorString 자체가 아닌)이 error 인터페이스를 만족하는 이유는, New를 호출할 때마다 다른 에러와 같지 않은 별개의 에러 인스턴스가 할당되도록 하기 위해서입니다. diff --git a/locales/ko/218-The-error-Interface/error-3.md b/locales/ko/218-The-error-Interface/error-3.md new file mode 100644 index 0000000..6e69e0b --- /dev/null +++ b/locales/ko/218-The-error-Interface/error-3.md @@ -0,0 +1 @@ +io.EOF와 같은 특별한 에러가 단지 동일한 메시지를 가진 에러와 같다고 비교되는 것을 원하지 않습니다. diff --git a/locales/ko/218-The-error-Interface/error-4.md b/locales/ko/218-The-error-Interface/error-4.md new file mode 100644 index 0000000..4c1edea --- /dev/null +++ b/locales/ko/218-The-error-Interface/error-4.md @@ -0,0 +1 @@ +errors.New를 직접 호출하는 경우는 비교적 드문데, 문자열 포매팅도 수행하는 편리한 래퍼 함수 fmt.Errorf가 있기 때문입니다. diff --git a/locales/ko/219-Type-Assertions/type-assert-1.md b/locales/ko/219-Type-Assertions/type-assert-1.md new file mode 100644 index 0000000..c7f4eb7 --- /dev/null +++ b/locales/ko/219-Type-Assertions/type-assert-1.md @@ -0,0 +1,3 @@ +타입 단언은 인터페이스 값에 적용되는 연산입니다. 구문적으로 x.(T)와 같은 형태이며, 여기서 x는 인터페이스 타입의 표현식이고 T는 "단언" 타입이라고 하는 타입입니다. 타입 단언은 피연산자의 동적 타입이 단언된 타입과 일치하는지 검사합니다. +--- +두 가지 경우가 있습니다. 첫째, 단언 타입 T가 구체적 타입인 경우, 타입 단언은 x의 동적 타입이 T와 동일한지 검사합니다. 검사가 성공하면 타입 단언의 결과는 x의 동적 값이며, 그 타입은 당연히 T입니다. 즉, 구체적 타입에 대한 타입 단언은 피연산자에서 구체적 값을 추출합니다. 검사가 실패하면 연산은 panic을 발생시킵니다. 예를 들어: diff --git a/locales/ko/219-Type-Assertions/type-assert-2.md b/locales/ko/219-Type-Assertions/type-assert-2.md new file mode 100644 index 0000000..bbf861b --- /dev/null +++ b/locales/ko/219-Type-Assertions/type-assert-2.md @@ -0,0 +1 @@ +둘째, 단언 타입 T가 인터페이스 타입인 경우, 타입 단언은 x의 동적 타입이 T를 만족하는지 검사합니다. 검사가 성공하면 동적 값은 추출되지 않으며, 결과는 여전히 동일한 타입과 값 컴포넌트를 가진 인터페이스 값이지만 인터페이스 타입 T를 갖습니다. 즉, 인터페이스 타입에 대한 타입 단언은 표현식의 타입을 변경하여 다른(보통 더 큰) 메서드 집합에 접근할 수 있게 하지만, 인터페이스 값 내부의 동적 타입과 값 컴포넌트는 보존합니다. diff --git a/locales/ko/219-Type-Assertions/type-assert-3.md b/locales/ko/219-Type-Assertions/type-assert-3.md new file mode 100644 index 0000000..9ffbb81 --- /dev/null +++ b/locales/ko/219-Type-Assertions/type-assert-3.md @@ -0,0 +1 @@ +어떤 타입을 단언하든, 피연산자가 nil 인터페이스 값이면 타입 단언은 실패합니다. 메서드가 더 적은 인터페이스 타입에 대한 타입 단언은 거의 필요하지 않으며, nil인 경우를 제외하면 할당과 동일하게 동작합니다. diff --git a/locales/ko/219-Type-Assertions/type-assert-4.md b/locales/ko/219-Type-Assertions/type-assert-4.md new file mode 100644 index 0000000..51fda87 --- /dev/null +++ b/locales/ko/219-Type-Assertions/type-assert-4.md @@ -0,0 +1,3 @@ +종종 인터페이스 값의 동적 타입을 확신할 수 없어, 특정 타입인지 테스트하고 싶을 때가 있습니다. 타입 단언이 두 개의 결과를 기대하는 대입에 나타나면, 예를 들어 다음 선언과 같이, 실패 시 panic이 발생하지 않고 대신 성공 여부를 나타내는 추가적인 두 번째 결과인 불리언을 반환합니다: +--- +두 번째 결과는 관례적으로 ok라는 이름의 변수에 할당됩니다. 연산이 실패하면 ok는 false이고, 첫 번째 결과는 단언 타입의 제로 값과 같으며, 이 예제에서는 nil *bytes.Buffer입니다. diff --git a/locales/ko/220-Type-Switches/type-switch.md b/locales/ko/220-Type-Switches/type-switch.md new file mode 100644 index 0000000..20e1ef6 --- /dev/null +++ b/locales/ko/220-Type-Switches/type-switch.md @@ -0,0 +1 @@ +타입 `switch`는 값이 아닌 타입을 비교합니다. 이를 사용하여 인터페이스 값의 타입을 판별할 수 있습니다. 이 예제에서 변수 `t`는 해당 절에 대응하는 타입이 됩니다. diff --git a/locales/zh.json b/locales/zh.json new file mode 100644 index 0000000..c3f4458 --- /dev/null +++ b/locales/zh.json @@ -0,0 +1,60 @@ +{ + "site_title": "XGo 教程", + "breadcrumb_tutorials": "教程", + "index_heading": "教程", + "index_desc_1": "XGo 是一门开源编程语言,旨在让每个人都能成为世界的构建者。", + "index_desc_2": "XGo 教程是一个通过带注释的示例程序来学习 XGo 的实践教程。查看第一个示例,或浏览下方完整列表。", + "no_content_before": "暂无内容,你可以在", + "no_content_link": "这里", + "no_content_after": "帮助我们完善。", + "next_example": "下一个示例:", + "lang_switcher_label": "切换语言", + "titles": { + "Sequential programming": "Sequential Programming 顺序编程", + "Structured programming": "Structured Programming 结构化编程", + "Hello world": "Hello World", + "Values": "Values 值", + "Constants": "Constants 常量", + "Variables": "Variables 变量", + "Assignments": "Assignments 赋值", + "Types": "Types 类型", + "Integers": "Integers 整数", + "Floating-Point Numbers": "Floating-Point Numbers 浮点数", + "Complex Numbers": "Complex Numbers 复数", + "Booleans": "Booleans 布尔值", + "Strings": "Strings 字符串", + "Rational Numbers": "Rational Numbers 有理数", + "If/Else": "If/Else 条件判断", + "Switch": "Switch 分支选择", + "For": "For 循环", + "Arrays": "Arrays 数组", + "Slices": "Slices 切片", + "Maps": "Maps 映射", + "Structs": "Structs 结构体", + "Pointers": "Pointers 指针", + "For Each": "For Each 遍历", + "For Range": "For Range 范围遍历", + "List Comprehension": "List Comprehension 列表推导式", + "Functions": "Functions 函数", + "Multiple Return Values": "Multiple Return Values 多返回值", + "Errors": "Errors 错误处理", + "Function Values": "Function Values 函数值", + "Closures": "Closures 闭包", + "Lambda expressions": "Lambda Expressions Lambda 表达式", + "Recursion": "Recursion 递归", + "Variadic Parameters": "Variadic Parameters 可变参数", + "Defer": "Defer 延迟执行", + "Exceptions": "Exceptions 异常处理", + "Methods": "Methods 方法", + "Methods with a Pointer Receiver": "Methods with a Pointer Receiver 指针接收者方法", + "Composing Types by Struct Embedding": "Composing Types by Struct Embedding 通过结构体嵌入组合类型", + "Method Values and Expressions": "Method Values and Expressions 方法值与方法表达式", + "Encapsulation": "Encapsulation 封装", + "Interfaces": "Interfaces 接口", + "Interface Satisfaction": "Interface Satisfaction 接口实现", + "Interface Values": "Interface Values 接口值", + "The error Interface": "The error Interface 错误接口", + "Type Assertions": "Type Assertions 类型断言", + "Type Switches": "Type Switches 类型分支" + } +} diff --git a/locales/zh/101-Hello-world/hello-11.md b/locales/zh/101-Hello-world/hello-11.md new file mode 100644 index 0000000..4f88f77 --- /dev/null +++ b/locales/zh/101-Hello-world/hello-11.md @@ -0,0 +1,5 @@ +XGo 的 Hello World 有三种写法。 +--- +### 第一种:命令式风格 +--- +这是我们推荐的写法,非常容易理解。特别是对中小学生来说,命令是最容易理解的逻辑,比函数调用容易得多。 diff --git a/locales/zh/101-Hello-world/hello-12.md b/locales/zh/101-Hello-world/hello-12.md new file mode 100644 index 0000000..8e19d22 --- /dev/null +++ b/locales/zh/101-Hello-world/hello-12.md @@ -0,0 +1 @@ +为了强调我们对命令式风格的偏好,我们引入了 `echo` 作为 `println` 的别名: diff --git a/locales/zh/101-Hello-world/hello-2.md b/locales/zh/101-Hello-world/hello-2.md new file mode 100644 index 0000000..4337490 --- /dev/null +++ b/locales/zh/101-Hello-world/hello-2.md @@ -0,0 +1,3 @@ +## 第二种:函数调用风格 +--- +这种写法更像 Python。理解这种写法的基础是理解什么是函数调用。这并不太难,特别是如果中学生在数学课上学过函数(如 sin),就比较容易理解。大多数编程语言也支持这种标准的函数调用语法。 diff --git a/locales/zh/101-Hello-world/hello-3.md b/locales/zh/101-Hello-world/hello-3.md new file mode 100644 index 0000000..7c2943f --- /dev/null +++ b/locales/zh/101-Hello-world/hello-3.md @@ -0,0 +1,13 @@ +## 第三种:软件工程风格 +--- +这是从 Go 语言继承而来的标准软件工程写法。对初学者来说不太容易理解,因为需要理解什么是函数(func)和什么是包(package)。当然,它也有其优点——让你开始建立功能分解和团队协作的基本逻辑。 +--- +那么,如何体验 XGo 呢? +--- +最简单的方式,直接去 XGo Playground 体验: +--- +* https://play.xgo.dev +--- +在早期学习基础语法时,完全可以用这种方式。 +--- +如何在本地安装 XGo?我们后面会讨论这个话题。 diff --git a/locales/zh/102-Values/values.md b/locales/zh/102-Values/values.md new file mode 100644 index 0000000..ffd3d5f --- /dev/null +++ b/locales/zh/102-Values/values.md @@ -0,0 +1,9 @@ +XGo 有多种值类型,包括字符串、整数、浮点数、布尔值等。 +--- +以下是一些基本示例。 +--- +字符串,可以使用 `+` 进行拼接。 +--- +整数和浮点数。 +--- +布尔值,以及常见的布尔运算符。 diff --git a/locales/zh/103-Constants/constants.md b/locales/zh/103-Constants/constants.md new file mode 100644 index 0000000..0964111 --- /dev/null +++ b/locales/zh/103-Constants/constants.md @@ -0,0 +1,11 @@ +XGo 支持字符、字符串、布尔值和数值类型的_常量_。 +--- +`const` 用于声明一个常量值。 +--- +`const` 语句可以出现在任何 `var` 语句可以出现的地方。 +--- +常量表达式可以执行任意精度的算术运算。 +--- +数值常量在被显式转换等方式赋予类型之前,是没有类型的。 +--- +数值可以通过在需要特定类型的上下文中使用来获得类型,例如变量赋值或函数调用。例如,这里 `math.sin` 期望一个 `float64` 类型的参数。 diff --git a/locales/zh/104-Variables/vars.md b/locales/zh/104-Variables/vars.md new file mode 100644 index 0000000..a228b5d --- /dev/null +++ b/locales/zh/104-Variables/vars.md @@ -0,0 +1,11 @@ +在 XGo 中,_变量_ 需要显式声明,编译器会利用它们来检查函数调用的类型正确性等。 +--- +`var` 用于声明一个或多个变量。 +--- +你可以一次声明多个变量。 +--- +XGo 会自动推断已初始化变量的类型。 +--- +声明时未进行初始化的变量会被赋予_零值_。例如,`int` 的零值是 `0`。 +--- +`:=` 语法是声明并初始化变量的简写形式,例如在这个例子中等价于 `var f string = "apple"`。 diff --git a/locales/zh/105-Assignments/assign-1.md b/locales/zh/105-Assignments/assign-1.md new file mode 100644 index 0000000..a1338bf --- /dev/null +++ b/locales/zh/105-Assignments/assign-1.md @@ -0,0 +1,3 @@ +### 赋值 +--- +`=` 用于赋值。可以在一行中同时修改多个变量的值。通过这种方式,可以在不使用中间变量的情况下交换它们的值。 diff --git a/locales/zh/105-Assignments/assign-2.md b/locales/zh/105-Assignments/assign-2.md new file mode 100644 index 0000000..8ea1d0c --- /dev/null +++ b/locales/zh/105-Assignments/assign-2.md @@ -0,0 +1,3 @@ +### `=` 与 `:=` +--- +`:=` 用于声明并初始化变量。可以在一行中声明并初始化多个变量。 diff --git a/locales/zh/106-Types/types-2.md b/locales/zh/106-Types/types-2.md new file mode 100644 index 0000000..44f514c --- /dev/null +++ b/locales/zh/106-Types/types-2.md @@ -0,0 +1,7 @@ +### XGo 内置了对有理数的支持: + + +我们将有理数作为 XGo 的基本类型引入。我们使用后缀 `r` 来表示有理数字面量。例如,`1r << 200` 表示一个值等于 2^200 的大整数。 + +默认情况下,`1r` 的类型为 bigint。 +而 `4/5r` 表示有理常量 4/5,其类型为 bigrat。 diff --git a/locales/zh/106-Types/types.md b/locales/zh/106-Types/types.md new file mode 100644 index 0000000..612ee59 --- /dev/null +++ b/locales/zh/106-Types/types.md @@ -0,0 +1,19 @@ +### XGo 的基本类型包括 +
+bool
+int8    int16   int32   int    int64    int128
+uint8   uint16  uint32  uint   uint64   uint128
+uintptr (类似于 C 语言的 size_t)
+byte (uint8 的别名)
+rune (int32 的别名,表示一个 Unicode 码点)
+string
+float32 float64
+complex64 complex128
+bigint bigrat
+unsafe.Pointer (类似于 C 语言的 void*)
+any (Go 中 interface{} 的别名)
+
+ +示例展示了多种类型的变量,同时也演示了变量声明可以像 import 语句一样被"分组"到代码块中。 +--- +int、uint 和 uintptr 类型在 32 位系统上通常是 32 位宽,在 64 位系统上是 64 位宽。当你需要一个整数值时,应该使用 int,除非有特定原因需要使用固定大小或无符号的整数类型。 diff --git a/locales/zh/107-Integers/integers.md b/locales/zh/107-Integers/integers.md new file mode 100644 index 0000000..675ea2d --- /dev/null +++ b/locales/zh/107-Integers/integers.md @@ -0,0 +1,8 @@ +int 类型表示整数,可以是正数或负数。int 类型的大小取决于平台,可以是 32 位或 64 位。还有具有特定大小的整数类型,如 int8、int16、int32、int64 和 int128,但除非你需要特定大小,否则应该使用 int 类型。 + +uint 类型表示正整数。uint 类型的大小取决于平台,可以是 32 位或 64 位。还有具有特定大小的无符号整数类型,如 uint8、uint16、uint32、uint64 和 uint128,但除非你需要特定大小,否则应该使用 uint 类型。 + +对于 int 值 20,也可以用十六进制 (0x14)、八进制 (0o24) 和二进制 (0b0010100) 表示。 +uint 没有 uint 字面量,所有整数字面量都被视为 int 值。 + +XGo 还支持使用 `_` 作为数字分隔符,也支持将 bool 转换为数值类型。如示例所示 diff --git a/locales/zh/108-Floating-Point-Numbers/numbers.md b/locales/zh/108-Floating-Point-Numbers/numbers.md new file mode 100644 index 0000000..83442c1 --- /dev/null +++ b/locales/zh/108-Floating-Point-Numbers/numbers.md @@ -0,0 +1,8 @@ +XGo 中有两种浮点类型:float32 和 float64。 +浮点数字面量的默认类型为 float64。 + +浮点数无法精确表示十进制值。不要用它们来表示货币或任何其他需要精确十进制表示的值! + +虽然你可以使用 == 和 != 来比较浮点数,但不建议这样做。由于浮点数的不精确性,两个浮点值可能在你认为它们应该相等时并不相等。相反,应该定义一个最小允许偏差,然后检查两个浮点数的差值是否小于该值。这个最小值(有时称为 epsilon)取决于你的精度需求; + +浮点数字面量也可以用 10 的幂来声明,将一个值为 0 的浮点变量除以 0 会返回 NaN(非数字)。 diff --git a/locales/zh/109-Complex-Numbers/complex-1.md b/locales/zh/109-Complex-Numbers/complex-1.md new file mode 100644 index 0000000..5980b43 --- /dev/null +++ b/locales/zh/109-Complex-Numbers/complex-1.md @@ -0,0 +1,3 @@ +### 初始化复数 +XGo 中有两种复数类型:complex64 和 complex128。 +初始化复数非常简单。你可以使用构造函数或初始化语法。 diff --git a/locales/zh/109-Complex-Numbers/complex-2.md b/locales/zh/109-Complex-Numbers/complex-2.md new file mode 100644 index 0000000..dea7922 --- /dev/null +++ b/locales/zh/109-Complex-Numbers/complex-2.md @@ -0,0 +1,2 @@ +### 复数的组成部分 +复数有两个部分:实部和虚部。我们使用函数来获取它们。 diff --git a/locales/zh/109-Complex-Numbers/complex-3.md b/locales/zh/109-Complex-Numbers/complex-3.md new file mode 100644 index 0000000..86cd6a6 --- /dev/null +++ b/locales/zh/109-Complex-Numbers/complex-3.md @@ -0,0 +1,4 @@ +### 复数的运算 +复数变量可以执行加法、减法、乘法和除法等任何运算。 + +让我们来看一个对复数执行数学运算的示例。 diff --git a/locales/zh/110-Booleans/boolean-1.md b/locales/zh/110-Booleans/boolean-1.md new file mode 100644 index 0000000..cbc118d --- /dev/null +++ b/locales/zh/110-Booleans/boolean-1.md @@ -0,0 +1 @@ +bool 类型表示布尔变量。bool 类型的变量只能有两个值:true 或 false。bool 的零值为 false。 diff --git a/locales/zh/110-Booleans/boolean-2.md b/locales/zh/110-Booleans/boolean-2.md new file mode 100644 index 0000000..d15de3f --- /dev/null +++ b/locales/zh/110-Booleans/boolean-2.md @@ -0,0 +1 @@ +在 XGo 中,你可以将 bool 转换为数值类型。 diff --git a/locales/zh/111-Strings/strings.md b/locales/zh/111-Strings/strings.md new file mode 100644 index 0000000..0282b43 --- /dev/null +++ b/locales/zh/111-Strings/strings.md @@ -0,0 +1,21 @@ +字符串是一个不可变的字节序列。字符串可以包含任何数据,通常用于保存文本。 + +我们一般使用双引号 "" 来定义字符串,但需要注意其中有些特定字符具有特殊含义,我们称之为转义字符,这些转义字符包括: + +
+  \n:换行
+  \r:回车
+  \t:制表符
+  \u 或 \U:Unicode
+  \:反斜杠
+
+--- +如果我们想知道字符串占用的字节长度,可以使用 XGo 的内置函数来计算: +--- +如果我们要定义一个字符串,语法如下: +--- +我们可以使用 + 将两个字符串拼接在一起,将后面的字符串追加到前面字符串的末尾。 +--- +如果我们想定义多行字符串,XGo 也支持。使用传统的 "" 无法跨行,如果要定义多行字符串,可以使用反引号:` +--- +反引号之间的代码不会被编辑器识别为特殊字符,而只会作为字符串的一部分。 diff --git a/locales/zh/112-Rational-Numbers/rational-1.md b/locales/zh/112-Rational-Numbers/rational-1.md new file mode 100644 index 0000000..9b4ceaa --- /dev/null +++ b/locales/zh/112-Rational-Numbers/rational-1.md @@ -0,0 +1,17 @@ +XGo 拥有多种有理数类型,包括 bigint、bigrat 和 bigfloat。 +以下是一些关于 bigint 类型的基本示例。 +--- +### bigint 类型变量的声明与赋值 +--- +XGo 语言使用关键字 "var" 来声明变量。 +--- +整数有理变量可以在声明时赋值。 +(1r<<65),其值等于 2 的 65 次方。 +--- +注意: +未赋值的有理变量的初始值不是 0,而是 ``。 + +预期结果: +bint1: `` +bint2: `` +bint3: `36893488147419103232` diff --git a/locales/zh/113-If/Else/if-else.md b/locales/zh/113-If/Else/if-else.md new file mode 100644 index 0000000..f408121 --- /dev/null +++ b/locales/zh/113-If/Else/if-else.md @@ -0,0 +1,9 @@ +在 XGo 中使用 `if` 和 `else` 进行分支判断非常简单直接。 +--- +这是一个基本示例。 +--- +你可以使用不带 else 的 `if` 语句。 +--- +条件语句之前可以有一个前置语句;在该语句中声明的变量在所有分支中都可用。 +--- +注意,在 XGo 中条件表达式不需要用圆括号括起来,但花括号是必须的。 diff --git a/locales/zh/113-Switch/switch-1.md b/locales/zh/113-Switch/switch-1.md new file mode 100644 index 0000000..73eb304 --- /dev/null +++ b/locales/zh/113-Switch/switch-1.md @@ -0,0 +1,11 @@ +_Switch 语句_ 用于表达跨多个分支的条件判断。 +--- +这是一个基本的 `switch` 示例。 +--- +你可以在同一个 `case` 语句中使用逗号分隔多个表达式。在这个示例中我们还使用了可选的 `default` 分支。 +--- +不带表达式的 `switch` 是表达 if/else 逻辑的另一种方式。这里我们还展示了 `case` 表达式可以是非常量的。 +--- +XGo 中的 switch 默认在每个 case 末尾自动 break。使用 fallthrough 可以强制执行后续 case 的代码。 +--- +带有 `fallthrough` 的 `switch`: diff --git a/locales/zh/114-For/for-0.md b/locales/zh/114-For/for-0.md new file mode 100644 index 0000000..8b26d1b --- /dev/null +++ b/locales/zh/114-For/for-0.md @@ -0,0 +1,2 @@ +### For 循环 +XGo 只有一个循环关键字:for,但有多种形式。 diff --git a/locales/zh/114-For/for-1.md b/locales/zh/114-For/for-1.md new file mode 100644 index 0000000..d144f31 --- /dev/null +++ b/locales/zh/114-For/for-1.md @@ -0,0 +1,6 @@ +### 1、for/in +这是最常见的形式。你可以将它用于切片、映射、数值范围或自定义迭代器。 + +`for value in arr/map` 形式用于遍历切片或映射的元素。 + +如果需要索引,可以使用替代形式 `for index, value in arr`。 diff --git a/locales/zh/114-For/for-2.md b/locales/zh/114-For/for-2.md new file mode 100644 index 0000000..a17740d --- /dev/null +++ b/locales/zh/114-For/for-2.md @@ -0,0 +1,2 @@ +### 2、Range for +你可以在 for 循环中使用范围表达式 (start:end:step)。 diff --git a/locales/zh/114-For/for-3.md b/locales/zh/114-For/for-3.md new file mode 100644 index 0000000..99ad6f4 --- /dev/null +++ b/locales/zh/114-For/for-3.md @@ -0,0 +1,2 @@ +### 3、for/in/if +所有 for/in 形式的循环都可以带有可选的 if 条件。 diff --git a/locales/zh/114-For/for-4.md b/locales/zh/114-For/for-4.md new file mode 100644 index 0000000..7462599 --- /dev/null +++ b/locales/zh/114-For/for-4.md @@ -0,0 +1,2 @@ +### 4、条件 for +可以省略条件,从而形成无限循环。你可以使用 break 或 return 来结束循环。 diff --git a/locales/zh/114-For/for-5.md b/locales/zh/114-For/for-5.md new file mode 100644 index 0000000..e30ca6b --- /dev/null +++ b/locales/zh/114-For/for-5.md @@ -0,0 +1,2 @@ +### 5、C 风格 for +最后是传统的 C 风格 for 循环。它比 while 形式更安全,因为使用 while 时很容易忘记更新计数器而陷入无限循环。 diff --git a/locales/zh/114-For/for-6.md b/locales/zh/114-For/for-6.md new file mode 100644 index 0000000..c9a389b --- /dev/null +++ b/locales/zh/114-For/for-6.md @@ -0,0 +1,4 @@ +### break 和 continue +如何在不使用键盘或关闭电脑的情况下跳出无限 for 循环?这就是 break 语句的作用。它会立即退出循环,就像其他语言中的 break 语句一样。当然,你可以在任何 for 语句中使用 break,而不仅仅是无限 for 语句。 + +XGo 还包含 continue 关键字,它会跳过 for 循环体的剩余部分,直接进入下一次迭代。从技术上讲,你并不一定需要 continue 语句。 diff --git a/locales/zh/114-For/for-7.md b/locales/zh/114-For/for-7.md new file mode 100644 index 0000000..6825bfa --- /dev/null +++ b/locales/zh/114-For/for-7.md @@ -0,0 +1,2 @@ +### 给 "for" 语句添加标签 +默认情况下,break 和 continue 关键字作用于直接包含它们的 for 循环。如果你有嵌套的 for 循环,想要退出或跳过外层循环的某次迭代怎么办?让我们来看一个示例。我们将修改字符串遍历程序,使其在遇到字母 "l" 时立即停止遍历该字符串。 diff --git a/locales/zh/116-Arrays/arrays1.md b/locales/zh/116-Arrays/arrays1.md new file mode 100644 index 0000000..3765fb8 --- /dev/null +++ b/locales/zh/116-Arrays/arrays1.md @@ -0,0 +1,13 @@ +在 XGo 中,_数组_ 是一个具有特定长度的、由相同类型元素组成的编号序列。 +--- +### 一维数组的声明 +--- +这里我们创建了一个恰好可以容纳 5 个 `int` 的数组 `a`。元素类型和长度都是数组类型的一部分。默认情况下,数组是零值的,对于 `int` 来说就是 `0`。 +--- +我们可以使用 `array[index] = value` 语法在某个索引位置设置值,使用 `array[index]` 获取值。 +--- +内置函数 `len` 返回数组的长度。 +--- +使用这种语法可以在一行中声明并初始化一个数组。 +--- +如果你不想手动写出数组的长度,可以使用这种方式,让编译器自动计算数组的长度。 diff --git a/locales/zh/116-Arrays/arrays2.md b/locales/zh/116-Arrays/arrays2.md new file mode 100644 index 0000000..dbc4c18 --- /dev/null +++ b/locales/zh/116-Arrays/arrays2.md @@ -0,0 +1,5 @@ +### 多维数组的声明 +--- +数组类型是一维的,但你可以组合类型来构建多维数据结构。 +--- +如果需要声明更多维度,可以自行扩展,例如声明一个 2*2*3 的三维数组 diff --git a/locales/zh/116-Arrays/arrays3.md b/locales/zh/116-Arrays/arrays3.md new file mode 100644 index 0000000..6336aea --- /dev/null +++ b/locales/zh/116-Arrays/arrays3.md @@ -0,0 +1,7 @@ +#### 提示 +--- +如果在 XGo 中不声明数组的内容,编译器会自动将数组初始化为 0;对于 bool 类型的数组,初始值为 false。 +--- +关于更多数组值类型,XGo 支持字符串、整数、浮点数、布尔值等。详情请参阅本章: +--- +* https://tutorial.xgo.dev/values diff --git a/locales/zh/117-Slices/slices-01.md b/locales/zh/117-Slices/slices-01.md new file mode 100644 index 0000000..ff9df25 --- /dev/null +++ b/locales/zh/117-Slices/slices-01.md @@ -0,0 +1,4 @@ +### XGo 风格的切片字面量 +切片是相同类型数据元素的集合。切片字面量是由方括号包围的表达式列表。可以使用索引表达式访问单个元素。索引从 0 开始。 + +在 XGo 中,你可以直接通过 len 方法获取切片长度,也可以对切片字面量进行类型转换: diff --git a/locales/zh/117-Slices/slices-02.md b/locales/zh/117-Slices/slices-02.md new file mode 100644 index 0000000..474b5b3 --- /dev/null +++ b/locales/zh/117-Slices/slices-02.md @@ -0,0 +1,6 @@ +### 切片 +数组具有固定大小。而切片是对数组元素的动态大小、灵活的视图。在实践中,切片比数组更常用。 + +类型 []T 是一个元素类型为 T 的切片。 + +切片通过指定两个索引(上界和下界)来形成,两者之间用冒号分隔:a[low : high]。它选择一个半开区间,包含第一个元素,但不包含最后一个元素。 diff --git a/locales/zh/117-Slices/slices-03.md b/locales/zh/117-Slices/slices-03.md new file mode 100644 index 0000000..95c2c78 --- /dev/null +++ b/locales/zh/117-Slices/slices-03.md @@ -0,0 +1,6 @@ +### 切片类似于数组的引用 +切片不存储任何数据,它只是描述了底层数组的一个片段。 + +修改切片的元素会修改其底层数组中对应的元素。 + +共享同一底层数组的其他切片会看到这些更改。 diff --git a/locales/zh/117-Slices/slices-04.md b/locales/zh/117-Slices/slices-04.md new file mode 100644 index 0000000..f52cce6 --- /dev/null +++ b/locales/zh/117-Slices/slices-04.md @@ -0,0 +1,8 @@ +### 切片字面量 +切片字面量类似于没有长度的数组字面量。 +
+这是一个数组字面量:
+[3]bool{true, true, false}
+而下面这种写法会创建与上面相同的数组,然后构建一个引用该数组的切片:
+[]bool{true, true, false}
+
diff --git a/locales/zh/117-Slices/slices-05.md b/locales/zh/117-Slices/slices-05.md new file mode 100644 index 0000000..9c5ba80 --- /dev/null +++ b/locales/zh/117-Slices/slices-05.md @@ -0,0 +1,13 @@ +### 切片的默认值 +在进行切片操作时,可以省略上界或下界来使用其默认值。 + +下界的默认值是 0,上界的默认值是切片的长度。 +
+对于数组
+var a [10]int
+以下切片表达式是等价的:
+a[0:10]
+a[:10]
+a[0:]
+a[:]
+
diff --git a/locales/zh/117-Slices/slices-06.md b/locales/zh/117-Slices/slices-06.md new file mode 100644 index 0000000..809088e --- /dev/null +++ b/locales/zh/117-Slices/slices-06.md @@ -0,0 +1,10 @@ +### 切片的长度和容量 +切片同时具有长度和容量。 + +切片的长度是它所包含的元素数量。 + +切片的容量是从切片的第一个元素开始计算,到底层数组末尾的元素数量。 + +切片 s 的长度和容量可以通过表达式 len(s) 和 cap(s) 来获取。 + +你可以通过重新切片来扩展切片的长度,前提是它有足够的容量。试着修改示例程序中的某个切片操作,使其超出容量,看看会发生什么。 diff --git a/locales/zh/117-Slices/slices-07.md b/locales/zh/117-Slices/slices-07.md new file mode 100644 index 0000000..9072ae1 --- /dev/null +++ b/locales/zh/117-Slices/slices-07.md @@ -0,0 +1,4 @@ +### nil 切片 +切片的零值是 nil。 + +nil 切片的长度和容量都是 0,并且没有底层数组。 diff --git a/locales/zh/117-Slices/slices-08.md b/locales/zh/117-Slices/slices-08.md new file mode 100644 index 0000000..52b8721 --- /dev/null +++ b/locales/zh/117-Slices/slices-08.md @@ -0,0 +1,12 @@ +### 使用 make 创建切片 +切片可以使用内置的 make 函数创建;这是创建动态大小数组的方式。 + +make 函数会分配一个零值数组,并返回一个引用该数组的切片: + +
+a := make([]int, 5)			// len(a)=5
+要指定容量,可以向 make 传递第三个参数:
+b := make([]int, 0, 5) 		// len(b)=0, cap(b)=5
+b = b[:cap(b)] 				// len(b)=5, cap(b)=5
+b = b[1:]      				// len(b)=4, cap(b)=4
+
diff --git a/locales/zh/117-Slices/slices-09.md b/locales/zh/117-Slices/slices-09.md new file mode 100644 index 0000000..36107a1 --- /dev/null +++ b/locales/zh/117-Slices/slices-09.md @@ -0,0 +1,3 @@ +### 切片的切片 +切片可以包含任何类型,包括其他切片。 +创建一个井字棋棋盘。 diff --git a/locales/zh/117-Slices/slices-10.md b/locales/zh/117-Slices/slices-10.md new file mode 100644 index 0000000..cfb1317 --- /dev/null +++ b/locales/zh/117-Slices/slices-10.md @@ -0,0 +1,10 @@ +### 向切片追加元素 +向切片追加新元素是很常见的操作,因此 XGo 提供了内置的 append 函数。内置包的文档中描述了 append 的用法。 + +func append(s []T, vs ...T) []T + +append 的第一个参数 s 是一个类型为 T 的切片,其余参数是要追加到切片中的 T 类型的值。 + +append 的返回值是一个切片,包含原始切片的所有元素加上新提供的值。 + +如果 s 的底层数组太小,无法容纳所有给定的值,则会分配一个更大的数组。返回的切片将指向新分配的数组。 diff --git a/locales/zh/117-Slices/slices-11.md b/locales/zh/117-Slices/slices-11.md new file mode 100644 index 0000000..ac13c28 --- /dev/null +++ b/locales/zh/117-Slices/slices-11.md @@ -0,0 +1,4 @@ +### Range +for 循环的 range 形式可以遍历切片或 map。 + +在遍历切片时,每次迭代会返回两个值。第一个是索引,第二个是该索引处元素的副本。 diff --git a/locales/zh/117-Slices/slices-12.md b/locales/zh/117-Slices/slices-12.md new file mode 100644 index 0000000..830fe29 --- /dev/null +++ b/locales/zh/117-Slices/slices-12.md @@ -0,0 +1,9 @@ +### Range 续 + +
+你可以通过赋值给 _ 来跳过索引或值。
+for i, _ := range pow
+for _, value := range pow
+如果只需要索引,可以省略第二个变量。
+for i := range pow
+
diff --git a/locales/zh/118-Maps/map-0.md b/locales/zh/118-Maps/map-0.md new file mode 100644 index 0000000..d51d81b --- /dev/null +++ b/locales/zh/118-Maps/map-0.md @@ -0,0 +1,17 @@ +_Maps_ 是 XGo 内置的[关联数据类型](http://en.wikipedia.org/wiki/Associative_array)(在其他语言中有时称为 _哈希_ 或 _字典_)。 +--- +### Map 基础 +--- +使用 `make(map[key-type]val-type)` 创建一个空的 map。 +--- +使用常见的 `name[key] = val` 语法设置键值对。 +--- +使用例如 `println` 打印 map 时,会显示其所有的键值对。 +--- +使用 `name[key]` 获取某个键对应的值。 +--- +内置函数 `len` 在用于 map 时,返回键值对的数量。 +--- +内置函数 `delete` 从 map 中删除键值对。 +--- +从 map 中获取值时,可选的第二个返回值指示该键是否存在于 map 中。这可以用来区分缺失的键和零值的键(如 `0` 或 `""`)。这里我们不需要值本身,所以用 _空白标识符_ `_` 忽略了它。 diff --git a/locales/zh/118-Maps/map-1.md b/locales/zh/118-Maps/map-1.md new file mode 100644 index 0000000..445e0b8 --- /dev/null +++ b/locales/zh/118-Maps/map-1.md @@ -0,0 +1,11 @@ +### XGo 风格的 Map 字面量 +--- +map[string]int +--- +map[string]float64 +--- +map[string]interface{} +--- +map[int]interface{} +--- +空 map 的类型是 map[string]interface{} diff --git a/locales/zh/118-Maps/map-2.md b/locales/zh/118-Maps/map-2.md new file mode 100644 index 0000000..31ddc7c --- /dev/null +++ b/locales/zh/118-Maps/map-2.md @@ -0,0 +1,13 @@ +### Go 风格的 Map 字面量 +--- +你也可以使用以下语法在同一行中声明并初始化一个新的 map。 +--- +{"Hello": 1, "xsw": 3} +--- +{"Hello": 1, "xsw": 3.4} +--- +{"Hello": 1, "xsw": "XGo"} +--- +{1: 1.4, 3: "XGo"} +--- +{} diff --git a/locales/zh/119-Structs/struct-1.md b/locales/zh/119-Structs/struct-1.md new file mode 100644 index 0000000..333ca08 --- /dev/null +++ b/locales/zh/119-Structs/struct-1.md @@ -0,0 +1,9 @@ +Map 是存储某些类型数据的便捷方式,但它有局限性。它不能定义 API,因为无法限制 map 只允许特定的键。map 中的所有值也必须是相同的类型。因此,map 不是在函数之间传递数据的理想方式。当你有需要组合在一起的相关数据时,应该定义一个结构体。 + +结构体类型使用关键字 type、结构体类型名称、关键字 struct 和一对花括号({})来定义。在花括号内,列出结构体的字段。就像在 var 声明中先写变量名再写变量类型一样,结构体字段也是先写字段名再写字段类型。 + +还需要注意的是,与 map 字面量不同,结构体声明中字段之间没有逗号分隔。你可以在函数内部或外部定义结构体类型。在函数内部定义的结构体类型只能在该函数内使用。从技术上讲,你可以在任何块级别定义结构体。 + +一旦声明了结构体类型,我们就可以定义该类型的变量。 +--- +这里我们使用了 var 声明。由于没有给 fred 赋值,它会获得 person 结构体类型的零值。零值结构体的每个字段都被设置为该字段的零值。 diff --git a/locales/zh/119-Structs/struct-2.md b/locales/zh/119-Structs/struct-2.md new file mode 100644 index 0000000..f9dac9a --- /dev/null +++ b/locales/zh/119-Structs/struct-2.md @@ -0,0 +1,4 @@ +### 非空结构体字面量有两种不同的风格。 +结构体字面量可以指定为花括号内以逗号分隔的字段值列表: +--- +使用这种结构体字面量格式时,必须为结构体中的每个字段指定一个值,并且值按照它们在结构体定义中声明的顺序赋给字段。 diff --git a/locales/zh/119-Structs/struct-3.md b/locales/zh/119-Structs/struct-3.md new file mode 100644 index 0000000..28932c3 --- /dev/null +++ b/locales/zh/119-Structs/struct-3.md @@ -0,0 +1,4 @@ + +第二种结构体字面量风格类似于 map 字面量风格: +--- +你可以使用结构体中字段的名称来指定值。使用这种风格时,你可以省略某些键并以任意顺序指定字段。未指定的字段会被设置为其零值。你不能混合使用两种结构体字面量风格;要么所有字段都使用键名指定,要么都不使用。对于所有字段总是被指定的小型结构体,使用较简单的结构体字面量风格即可。在其他情况下,使用键名方式。虽然更冗长,但它能清楚地表明哪个值被赋给了哪个字段,而无需参考结构体定义。这种方式也更易于维护。如果你在初始化结构体时没有使用字段名,而未来版本的结构体添加了额外的字段,你的代码将无法编译。 diff --git a/locales/zh/119-Structs/struct-4.md b/locales/zh/119-Structs/struct-4.md new file mode 100644 index 0000000..35afd86 --- /dev/null +++ b/locales/zh/119-Structs/struct-4.md @@ -0,0 +1,4 @@ + +结构体中的字段使用点号表示法来访问: +--- +就像我们使用方括号来读写 map 一样,我们使用点号表示法来读写结构体字段。 diff --git a/locales/zh/119-Structs/struct-5.md b/locales/zh/119-Structs/struct-5.md new file mode 100644 index 0000000..1a7e737 --- /dev/null +++ b/locales/zh/119-Structs/struct-5.md @@ -0,0 +1,6 @@ +### 匿名结构体 +你也可以声明一个变量实现某个结构体类型,而不必先给该结构体类型命名。这被称为匿名结构体。 +--- +在这个示例中,变量 person 和 pet 的类型是匿名结构体。你可以像对命名结构体类型一样,对匿名结构体进行字段的赋值和读取。就像你可以用结构体字面量初始化一个命名结构体的实例一样,你也可以对匿名结构体做同样的事情。你可能会想,什么时候需要一个只与单个实例关联的数据类型。有两种常见的情况适合使用匿名结构体。第一种是当你将外部数据转换为结构体,或将结构体转换为外部数据时(如 JSON 或 protocol buffers)。这被称为数据的序列化和反序列化。 + +编写测试是匿名结构体的另一个常见使用场景。 diff --git a/locales/zh/119-Structs/struct-6.md b/locales/zh/119-Structs/struct-6.md new file mode 100644 index 0000000..47e2f28 --- /dev/null +++ b/locales/zh/119-Structs/struct-6.md @@ -0,0 +1,9 @@ +### 结构体的比较和转换 +结构体是否可比较取决于其字段。完全由可比较类型组成的结构体是可比较的;包含切片或 map 字段的结构体则不可比较(正如我们将在后续章节中看到的,函数和通道字段也会使结构体不可比较)。 + +与 Python 或 Ruby 不同,没有可以重写的魔法方法来重新定义相等性,使 == 和 != 适用于不可比较的结构体。当然,你可以编写自己的函数来比较结构体。 + +就像 XGo 不允许在不同基本类型的变量之间进行比较一样,XGo 也不允许在表示不同类型结构体的变量之间进行比较。如果两个结构体的字段具有相同的名称、顺序和类型,XGo 允许你执行从一个结构体类型到另一个结构体类型的类型转换。让我们看看这意味着什么。给定以下结构体: +--- +我们可以使用类型转换将 secondPerson 的实例转换为 firstPerson,但不能将 thirdPerson 的实例转换为 firstPerson,因为字段的顺序不同。 +但我们不能使用 == 来比较 firstPerson 的实例和 secondPerson 或 thirdPerson 的实例,因为它们是不同的类型。 diff --git a/locales/zh/119-Structs/struct-7.md b/locales/zh/119-Structs/struct-7.md new file mode 100644 index 0000000..ca9c2b4 --- /dev/null +++ b/locales/zh/119-Structs/struct-7.md @@ -0,0 +1 @@ +匿名结构体在这方面有一个小的特殊之处:如果正在比较的两个结构体变量中至少有一个是匿名结构体类型,那么只要两个结构体的字段具有相同的名称、顺序和类型,就可以在不进行类型转换的情况下比较它们。同样,如果两个结构体的字段具有相同的名称、顺序和类型,你也可以在命名结构体类型和匿名结构体类型之间进行赋值。 diff --git a/locales/zh/120-Pointers/pointer-1.md b/locales/zh/120-Pointers/pointer-1.md new file mode 100644 index 0000000..1c7e026 --- /dev/null +++ b/locales/zh/120-Pointers/pointer-1.md @@ -0,0 +1,6 @@ +指针是一个值为内存地址的变量。指针使用取地址符(& 字符)定义,即地址运算符,后跟变量名。 + +指针的类型是固定的,这意味着当你创建一个指向 int 的指针时,你可以改变它所指向的值,但不能用它指向存储不同类型(如 float64)的内存地址。这个限制很重要,指针不仅仅是内存地址,而是可能存储特定类型值的内存地址。 + +指针的类型基于创建它的变量类型,前面加上星号(* 字符)。名为 second 的变量类型是 *int,因为它是通过对 first 变量应用地址运算符创建的,而 first 的值是 int 类型。当你看到类型 *int 时,你就知道这是一个值为存储 int 变量的内存地址的变量。 + diff --git a/locales/zh/120-Pointers/pointer-2.md b/locales/zh/120-Pointers/pointer-2.md new file mode 100644 index 0000000..d326ade --- /dev/null +++ b/locales/zh/120-Pointers/pointer-2.md @@ -0,0 +1,6 @@ +### 跟随指针 +"跟随指针"这个短语的意思是读取指针所引用的内存地址处的值,这是通过使用星号(* 字符)来完成的。星号告诉 XGo 跟随指针并获取该内存位置的值。这被称为解引用指针。 +--- +第一条新语句定义了一个新变量,这里使用 var 关键字来强调变量类型是 *int,即指向 int 值的指针。下一条语句将 second 变量的值赋给新变量,这意味着 second 和 myNewPointer 的值都是 first 值的内存位置。跟随任一指针都会访问相同的内存位置,这意味着递增 myNewPointer 会影响通过跟随 second 指针获得的值。 + +一个常见的误解是 first 和 second 变量具有相同的值,但事实并非如此。这里有两个值。一个是可以通过名为 first 的变量访问的 int 值。还有一个 *int 值,它存储了 first 值的内存位置。可以跟随 *int 值来访问存储的 int 值。但是,因为 *int 值本身也是一个值,所以它可以独立使用,这意味着它可以赋值给其他变量、用作调用函数的参数等。 diff --git a/locales/zh/120-Pointers/pointer-3.md b/locales/zh/120-Pointers/pointer-3.md new file mode 100644 index 0000000..41887f5 --- /dev/null +++ b/locales/zh/120-Pointers/pointer-3.md @@ -0,0 +1,6 @@ +### 理解指针的零值 +已定义但未赋值的指针的零值为 nil。 + +指针 second 已定义但未初始化值,并使用 println 函数输出。然后使用地址运算符创建指向 first 变量的指针,再次输出 second 的值。 + +如果你跟随一个未被赋值的指针,将会发生运行时错误。 diff --git a/locales/zh/120-Pointers/pointer-4.md b/locales/zh/120-Pointers/pointer-4.md new file mode 100644 index 0000000..9b55d45 --- /dev/null +++ b/locales/zh/120-Pointers/pointer-4.md @@ -0,0 +1,4 @@ +### 指向指针的指针 +由于指针存储内存位置,因此可以创建一个值为另一个指针内存地址的指针。 +--- +跟随指针链的语法可能比较笨拙。在这种情况下,需要两个星号。第一个星号跟随指针到内存位置,获取名为 second 的变量存储的值,即一个 *int 值。第二个星号跟随名为 second 的指针,从而访问 first 变量存储的值的内存位置。这不是你在大多数项目中需要做的事情,但它很好地确认了指针的工作方式以及如何沿着链路获取数据值。 diff --git a/locales/zh/121-For-Each/for-each-1.md b/locales/zh/121-For-Each/for-each-1.md new file mode 100644 index 0000000..3a6e686 --- /dev/null +++ b/locales/zh/121-For-Each/for-each-1.md @@ -0,0 +1 @@ +在 XGo 中没有 foreach 循环,但 for 循环可以用作 "foreach"。有一个关键字 range,你可以将 for 和 range 组合在一起,并可以选择在循环中使用键或值。 diff --git a/locales/zh/121-For-Each/for-each-2.md b/locales/zh/121-For-Each/for-each-2.md new file mode 100644 index 0000000..5c14db6 --- /dev/null +++ b/locales/zh/121-For-Each/for-each-2.md @@ -0,0 +1,6 @@ +### for-range 语句 +for-range 循环的有趣之处在于你会获得两个循环变量。第一个变量是正在迭代的数据结构中的位置,第二个是该位置的值。两个循环变量的惯用名称取决于正在遍历的内容。遍历数组、切片或字符串时,通常使用 i 表示索引。遍历 map 时,则使用 k(表示键)。 + +第二个变量通常被命名为 v 表示值,但有时也会根据被迭代值的类型来命名。 + +如果你不需要访问键,可以使用下划线(_)作为变量名。这告诉 XGo 忽略该值。 diff --git a/locales/zh/121-For-Each/for-each-3.md b/locales/zh/121-For-Each/for-each-3.md new file mode 100644 index 0000000..9aa4c86 --- /dev/null +++ b/locales/zh/121-For-Each/for-each-3.md @@ -0,0 +1,2 @@ +### 遍历 Map +for-range 循环遍历 map 的方式有一些有趣的地方。 diff --git a/locales/zh/121-For-Range/range.md b/locales/zh/121-For-Range/range.md new file mode 100644 index 0000000..9131c76 --- /dev/null +++ b/locales/zh/121-For-Range/range.md @@ -0,0 +1,11 @@ +_range_ 可以遍历多种数据结构中的元素。让我们看看如何将 `range` 与我们已经学过的一些数据结构一起使用。 +--- +这里我们使用 `range` 对切片中的数字求和。数组也可以这样使用。 +--- +对数组和切片使用 `range` 时,会同时提供每个条目的索引和值。上面我们不需要索引,所以用空白标识符 `_` 忽略了它。但有时我们确实需要索引。 +--- +对 map 使用 `range` 会遍历键值对。 +--- +`range` 也可以只遍历 map 的键。 +--- +对字符串使用 `range` 会遍历 Unicode 码点。第一个值是 `rune` 的起始字节索引,第二个值是 `rune` 本身。 diff --git a/locales/zh/121-List-Comprehension/listcompr.md b/locales/zh/121-List-Comprehension/listcompr.md new file mode 100644 index 0000000..1afd840 --- /dev/null +++ b/locales/zh/121-List-Comprehension/listcompr.md @@ -0,0 +1,9 @@ +生成奇数和偶数列表。 +--- +平方数 +--- +迭代 +--- +分割字符串 +--- +按指定字符分割字符串 diff --git a/locales/zh/201-Functions/funcs.md b/locales/zh/201-Functions/funcs.md new file mode 100644 index 0000000..68eb001 --- /dev/null +++ b/locales/zh/201-Functions/funcs.md @@ -0,0 +1,9 @@ +_函数_ 是 XGo 的核心。我们将通过几个不同的示例来学习函数。 +--- +这是一个接受两个 `int` 参数并返回它们之和(`int` 类型)的函数。 +--- +XGo 要求显式返回,即不会自动返回最后一个表达式的值。 +--- +当多个连续参数具有相同类型时,可以省略前面同类型参数的类型名称,只在最后一个参数处声明类型。 +--- +调用函数的方式与你预期的一样,使用 `name(args)`。 diff --git a/locales/zh/202-Multiple-Return-Values/multi-rets.md b/locales/zh/202-Multiple-Return-Values/multi-rets.md new file mode 100644 index 0000000..e85c9f0 --- /dev/null +++ b/locales/zh/202-Multiple-Return-Values/multi-rets.md @@ -0,0 +1,7 @@ +XGo 内置支持_多返回值_。这个特性在惯用的 XGo 代码中经常使用,例如同时返回函数的结果和错误值。 +--- +该函数签名中的 `(int, int)` 表示函数返回两个 `int` 值。 +--- +这里我们通过_多重赋值_来使用调用返回的两个不同值。 +--- +如果你只需要返回值的一部分,可以使用空白标识符 `_`。 diff --git a/locales/zh/203-Errors/errors.md b/locales/zh/203-Errors/errors.md new file mode 100644 index 0000000..334241a --- /dev/null +++ b/locales/zh/203-Errors/errors.md @@ -0,0 +1,15 @@ +在 XGo 中,惯用的做法是通过一个显式的、独立的返回值来传达错误。这与 Java 和 Ruby 等语言使用异常的方式不同,也不同于 C 语言中有时使用的重载单一结果/错误值的做法。XGo 的方式使得很容易看出哪些函数会返回错误,并使用与处理其他非错误任务相同的语言结构来处理它们。 +--- +按照惯例,错误是最后一个返回值,类型为 `error`,这是一个内置接口。 +--- +`errors.New` 使用给定的错误消息构造一个基本的 `error` 值。 +--- +错误位置的 `nil` 值表示没有错误。 +--- +可以通过在自定义类型上实现 `Error()` 方法来将其用作 `error`。以下是上面示例的一个变体,使用自定义类型来显式表示参数错误。 +--- +在这个例子中,我们使用 `&argError` 语法来构建一个新的结构体,为 `arg` 和 `prob` 两个字段提供值。 +--- +下面的两个循环测试了我们每个返回错误的函数。注意在 `if` 行中使用内联错误检查是 XGo 代码中的常见惯用法。 +--- +如果你想以编程方式使用自定义错误中的数据,需要通过类型断言将错误获取为自定义错误类型的实例。 diff --git a/locales/zh/204-Function Values/func-values.md b/locales/zh/204-Function Values/func-values.md new file mode 100644 index 0000000..c7d6146 --- /dev/null +++ b/locales/zh/204-Function Values/func-values.md @@ -0,0 +1 @@ +函数也是值。它们可以像其他值一样被传递。函数值可以用作函数参数和返回值。 diff --git a/locales/zh/205-Closures/closures.md b/locales/zh/205-Closures/closures.md new file mode 100644 index 0000000..08255b0 --- /dev/null +++ b/locales/zh/205-Closures/closures.md @@ -0,0 +1 @@ +XGo 函数可以是闭包。闭包是一个引用了其函数体外部变量的函数值。该函数可以访问和赋值所引用的变量;从这个意义上说,函数被"绑定"到了这些变量上。例如,adder 函数返回一个闭包。每个闭包都绑定到它自己的 sum 变量。 diff --git a/locales/zh/205-Lambda-expressions/lambda-expressions-1.md b/locales/zh/205-Lambda-expressions/lambda-expressions-1.md new file mode 100644 index 0000000..dd05a71 --- /dev/null +++ b/locales/zh/205-Lambda-expressions/lambda-expressions-1.md @@ -0,0 +1,3 @@ +Lambda 表达式用于在不给函数命名的情况下内联定义函数。 +--- +以下示例展示了 XGo 风格的 lambda 表达式,它更紧凑且易于理解。 diff --git a/locales/zh/205-Lambda-expressions/lambda-expressions-2.md b/locales/zh/205-Lambda-expressions/lambda-expressions-2.md new file mode 100644 index 0000000..9981239 --- /dev/null +++ b/locales/zh/205-Lambda-expressions/lambda-expressions-2.md @@ -0,0 +1 @@ +如果我们想定义一个 lambda 但不立即执行它,只需省略其标识符即可。例如: diff --git a/locales/zh/205-Lambda-expressions/lambda-expressions-3.md b/locales/zh/205-Lambda-expressions/lambda-expressions-3.md new file mode 100644 index 0000000..054519b --- /dev/null +++ b/locales/zh/205-Lambda-expressions/lambda-expressions-3.md @@ -0,0 +1 @@ +如果我们想定义 lambda 并立即执行它,需要省略其标识符,并在函数体的右花括号后面添加一个带括号的参数列表。例如: diff --git a/locales/zh/206-Recursion/recursion.md b/locales/zh/206-Recursion/recursion.md new file mode 100644 index 0000000..27d9d65 --- /dev/null +++ b/locales/zh/206-Recursion/recursion.md @@ -0,0 +1,6 @@ +XGo 编程语言支持递归,即允许函数调用自身。但在使用递归时,程序员需要注意定义函数的退出条件,否则将进入无限循环。 +递归函数在解决许多数学问题时非常有用,例如计算阶乘、生成斐波那契数列等。 +--- +本示例使用递归函数计算给定数字的阶乘 +--- +本示例展示如何使用递归函数生成给定数字的斐波那契数列 diff --git a/locales/zh/207-Variadic-Parameters/variadic.md b/locales/zh/207-Variadic-Parameters/variadic.md new file mode 100644 index 0000000..ff57c8f --- /dev/null +++ b/locales/zh/207-Variadic-Parameters/variadic.md @@ -0,0 +1 @@ +以可变数量参数调用的 joinstr 函数被称为可变参数函数。在 joinstr 函数的声明中,最后一个参数的类型前面带有省略号,即 (…)。这表示该函数可以接受任意数量的 string 参数。调用 joinstr(elements...) 时,如果不加 ...,将无法编译,因为类型不匹配;elements 不是 string 类型。 diff --git a/locales/zh/208-Defer/defer-1.md b/locales/zh/208-Defer/defer-1.md new file mode 100644 index 0000000..7f9dbfa --- /dev/null +++ b/locales/zh/208-Defer/defer-1.md @@ -0,0 +1,3 @@ +defer 语句会将函数的执行推迟到外层函数返回时。被推迟调用的函数参数会立即求值,但函数调用直到外层函数返回时才会执行。Defer 通常用于简化执行各种清理操作的函数。 +--- +defer 语句允许我们在打开文件后立即考虑关闭它,从而保证无论函数中有多少个 return 语句,文件都会被关闭。 diff --git a/locales/zh/208-Defer/defer-2.md b/locales/zh/208-Defer/defer-2.md new file mode 100644 index 0000000..b090940 --- /dev/null +++ b/locales/zh/208-Defer/defer-2.md @@ -0,0 +1,3 @@ +### defer 堆叠 +--- +被推迟的函数调用会被压入一个栈中。当函数返回时,其推迟的调用会按照后进先出的顺序执行。 diff --git a/locales/zh/209-Exceptions/exceptions-1.md b/locales/zh/209-Exceptions/exceptions-1.md new file mode 100644 index 0000000..e3a56f5 --- /dev/null +++ b/locales/zh/209-Exceptions/exceptions-1.md @@ -0,0 +1,5 @@ +### Panic +Panic 是一个内置函数,用于停止正常的执行流程。当你在代码中调用 panic 时,意味着你已经判定调用者无法解决该问题。因此,你应该仅在代码或集成你代码的人在该点继续执行不安全的极少数情况下使用 panic。 +以下代码示例演示了 panic 的工作方式: +--- +如上所示,当使用 panic 且未被处理时,执行流程将停止,所有被推迟的函数按相反顺序执行,并打印堆栈跟踪信息。 diff --git a/locales/zh/209-Exceptions/exceptions-2.md b/locales/zh/209-Exceptions/exceptions-2.md new file mode 100644 index 0000000..d393411 --- /dev/null +++ b/locales/zh/209-Exceptions/exceptions-2.md @@ -0,0 +1,5 @@ +### Recover +--- +要将错误作为返回值报告,你必须在调用 panic 函数的同一个 goroutine 中调用 recover 函数,从 recover 函数获取错误结构体,并将其传递给一个变量: +--- +每个被推迟的函数都会在函数调用之后、return 语句之前执行。因此,你可以在 return 语句执行之前设置返回变量的值。 diff --git a/locales/zh/210-Methods/methods-1.md b/locales/zh/210-Methods/methods-1.md new file mode 100644 index 0000000..cdfb8ee --- /dev/null +++ b/locales/zh/210-Methods/methods-1.md @@ -0,0 +1 @@ +XGo 没有类。但是,你可以在类型上定义方法。方法是一个带有特殊接收者参数的函数。接收者出现在 func 关键字和方法名之间的参数列表中。 diff --git a/locales/zh/210-Methods/methods-2.md b/locales/zh/210-Methods/methods-2.md new file mode 100644 index 0000000..3b80d19 --- /dev/null +++ b/locales/zh/210-Methods/methods-2.md @@ -0,0 +1,3 @@ +### 在非结构体类型上声明方法 +--- +在这个示例中,我们看到一个数值类型 MyFloat 带有一个 Abs 方法。你只能在与方法定义在同一个包中的类型上声明带接收者的方法。你不能在其他包中定义的类型上声明方法(包括 int 等内置类型)。 diff --git a/locales/zh/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.md b/locales/zh/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.md new file mode 100644 index 0000000..0cd9cd7 --- /dev/null +++ b/locales/zh/211-Methods-with-a-Pointer-Receiver/ptr-methods-1.md @@ -0,0 +1,11 @@ +### 指针接收者 +--- +你可以声明带指针接收者的方法。 +--- +这意味着接收者类型具有 *T 的字面语法,其中 T 是某个类型。(另外,T 本身不能是指针类型,如 *int。) +--- +例如,这里的 Scale 方法定义在 *Vertex 上。 +--- +带指针接收者的方法可以修改接收者所指向的值(如 Scale 所做的那样)。由于方法经常需要修改其接收者,因此指针接收者比值接收者更常用。 +--- +使用值接收者时,Scale 方法操作的是原始 Vertex 值的副本。(这与其他函数参数的行为相同。)Scale 方法必须使用指针接收者才能修改 Vertex 的值。 diff --git a/locales/zh/212-Composing-Types-by-Struct-Embedding/struct-emb-1.md b/locales/zh/212-Composing-Types-by-Struct-Embedding/struct-emb-1.md new file mode 100644 index 0000000..fb284f0 --- /dev/null +++ b/locales/zh/212-Composing-Types-by-Struct-Embedding/struct-emb-1.md @@ -0,0 +1,9 @@ +XGo 不提供典型的、基于类型驱动的子类化概念,但它可以通过在结构体或接口中嵌入类型来"借用"部分实现。只有接口可以嵌入到接口中。 +--- +示例中有两个结构体类型,Hello 结构体和 Goodbye 结构体,它们各自实现了 Talk 接口。HelloGoodbye 结构体也实现了 Talk 接口,它通过嵌入的方式将 Hello 结构体和 Goodbye 结构体组合到一个结构体中:在结构体中列出类型但不给它们字段名。 +--- +嵌入的元素是指向结构体的指针,当然在使用之前必须初始化为指向有效的结构体。 +--- +HelloGoodbye 结构体也有一个写作 forward *Forward 的 forward 成员,但如果要提升 forward 的方法并满足 Talk 接口,我们还需要提供转发方法,例如:hg.forward.Say()。通过直接嵌入结构体,我们避免了这种繁琐的操作。嵌入类型的方法会自动继承过来,这意味着 HelloGoodbye 结构体也拥有 Hello 结构体和 Goodbye 结构体的方法。 +--- +嵌入与子类化之间有一个重要区别。当我们嵌入一个类型时,该类型的方法成为外部类型的方法,但当它们被调用时,方法的接收者是内部类型,而不是外部类型。在我们的示例中,当 HelloGoodbye 的 Sleep 方法被调用时,它的效果与转发方法 helloGoodbye.Hello.Sleep() 完全相同;接收者是 helloGoodbye.Hello,而不是 helloGoodbye 本身。 diff --git a/locales/zh/212-Composing-Types-by-Struct-Embedding/struct-emb-2.md b/locales/zh/212-Composing-Types-by-Struct-Embedding/struct-emb-2.md new file mode 100644 index 0000000..3135f4e --- /dev/null +++ b/locales/zh/212-Composing-Types-by-Struct-Embedding/struct-emb-2.md @@ -0,0 +1,12 @@ +### 嵌入也可以是一种简单的便利方式。 +本示例展示了一个嵌入字段与一个常规的命名字段并列使用。 +--- +Job 类型现在拥有 *log.Logger 的 Print、Printf、Println 等方法。当然,我们可以给 Logger 一个字段名,但没有必要这样做。初始化之后,我们就可以向 Job 记录日志:job.Println("starting now...") +--- +Logger 是 Job 结构体的一个常规字段,因此我们可以在 Job 的构造函数中以通常的方式初始化它,就像 NewJob 函数所做的那样,或者使用复合字面量,job := &Job{command, log.New(os.Stderr, "Job: ", log.Ldate)} +--- +如果我们需要直接引用嵌入字段,忽略包限定符后的类型名就可以作为字段名使用,就像我们 Job 结构体的 Printf 方法中那样。这里,如果我们需要访问 Job 变量 job 的 *log.Logger,我们可以写 job.Logger,这在我们想要改进 Logger 方法时很有用。 +--- +嵌入类型会引入名称冲突的问题,但解决规则很简单。首先,字段或方法 X 会隐藏类型中更深层嵌套部分的任何其他项 X。如果 log.Logger 包含一个名为 Command 的字段或方法,Job 的 Command 字段将优先使用。 +--- +其次,如果相同的名称出现在相同的嵌套层级,这通常是一个错误;如果 Job 结构体包含另一个名为 Logger 的字段或方法,那么嵌入 log.Logger 将是错误的。但是,如果重复的名称在类型定义之外的程序中从未被提及,则是允许的。这一限定为防止从外部嵌入的类型发生更改提供了一定的保护;如果添加的字段与另一个子类型中的字段冲突,只要两个字段都没有被使用过,就不会有问题。 diff --git a/locales/zh/213-Method-Values-and-Expressions/method-values-1.md b/locales/zh/213-Method-Values-and-Expressions/method-values-1.md new file mode 100644 index 0000000..97354a5 --- /dev/null +++ b/locales/zh/213-Method-Values-and-Expressions/method-values-1.md @@ -0,0 +1,5 @@ +### 方法值 +--- +方法值允许你将方法绑定到特定的对象,然后像普通函数一样调用该方法,对象被隐含在内,类似于闭包。例如: +--- +表达式 hello.Say 创建了一个方法值,将 Say 函数绑定到特定的变量 hello,使其类型为 func()。因此函数值可以作为函数参数传递。 diff --git a/locales/zh/213-Method-Values-and-Expressions/method-values-2.md b/locales/zh/213-Method-Values-and-Expressions/method-values-2.md new file mode 100644 index 0000000..2f0fa97 --- /dev/null +++ b/locales/zh/213-Method-Values-and-Expressions/method-values-2.md @@ -0,0 +1,5 @@ +### 方法表达式 +--- +方法表达式可以将方法转换为一个以接收者作为第一个参数的函数。例如: +--- +因此,如果你定义了一个 Point 结构体和一个方法 func (p Point) Add(another Point),你可以写 Point.Add 来获得一个 func(p Point, another Point)。 diff --git a/locales/zh/213-Method-Values-and-Expressions/method-values-3.md b/locales/zh/213-Method-Values-and-Expressions/method-values-3.md new file mode 100644 index 0000000..b0fd017 --- /dev/null +++ b/locales/zh/213-Method-Values-and-Expressions/method-values-3.md @@ -0,0 +1 @@ +但如果方法有指针接收者,你需要在方法表达式中写成 (*Type).Method。例如: diff --git a/locales/zh/214-Encapsulation/encap-1.md b/locales/zh/214-Encapsulation/encap-1.md new file mode 100644 index 0000000..b02cf97 --- /dev/null +++ b/locales/zh/214-Encapsulation/encap-1.md @@ -0,0 +1,5 @@ +Golang 在包级别提供封装。Go 没有任何 public、private 或 protected 关键字。控制可见性的唯一机制是使用大写和小写格式。 +--- +大写标识符是导出的。大写字母表示这是一个导出的标识符。小写标识符是未导出的。小写字母表示该标识符未导出,只能在同一个包内访问。 +--- +有五种标识符可以被导出或不导出。 diff --git a/locales/zh/214-Encapsulation/encap-2.md b/locales/zh/214-Encapsulation/encap-2.md new file mode 100644 index 0000000..c404e1a --- /dev/null +++ b/locales/zh/214-Encapsulation/encap-2.md @@ -0,0 +1,3 @@ +### 1. 导出结构体 +--- +这里,在 company 包中,结构体 PublicCompany 是导出的,结构体 privateCompany 是未导出的。 diff --git a/locales/zh/214-Encapsulation/encap-3.md b/locales/zh/214-Encapsulation/encap-3.md new file mode 100644 index 0000000..a316a7c --- /dev/null +++ b/locales/zh/214-Encapsulation/encap-3.md @@ -0,0 +1,3 @@ +### 2. 导出结构体方法 +--- +Person 结构体的方法 GetAge() 是导出的,getName 是未导出的。 diff --git a/locales/zh/214-Encapsulation/encap-4.md b/locales/zh/214-Encapsulation/encap-4.md new file mode 100644 index 0000000..b6443e6 --- /dev/null +++ b/locales/zh/214-Encapsulation/encap-4.md @@ -0,0 +1,3 @@ +### 3. 导出结构体字段 +--- +Student 结构体的字段 Name 是导出的。Student 结构体的字段 age 是未导出的。 diff --git a/locales/zh/214-Encapsulation/encap-5.md b/locales/zh/214-Encapsulation/encap-5.md new file mode 100644 index 0000000..469d274 --- /dev/null +++ b/locales/zh/214-Encapsulation/encap-5.md @@ -0,0 +1,3 @@ +### 4. 导出函数 +--- +函数 Sum 是导出的,函数 average 是未导出的。 diff --git a/locales/zh/214-Encapsulation/encap-6.md b/locales/zh/214-Encapsulation/encap-6.md new file mode 100644 index 0000000..3265fdb --- /dev/null +++ b/locales/zh/214-Encapsulation/encap-6.md @@ -0,0 +1,3 @@ +### 5. 导出变量 +--- +变量 PersonName 是导出的,变量 personAge 是未导出的。 diff --git a/locales/zh/215-Interfaces/interfaces-1.md b/locales/zh/215-Interfaces/interfaces-1.md new file mode 100644 index 0000000..9a55b5c --- /dev/null +++ b/locales/zh/215-Interfaces/interfaces-1.md @@ -0,0 +1,5 @@ +首先,我们定义一个非常简单的几何图形接口,包含两个最基本的接口方法——计算面积和计算周长,代码如下: +--- +接下来,我们定义两个结构体,一个矩形结构体和一个圆形结构体,代码如下: +--- +然后分别实现上面定义的接口方法,与矩形结构体相关的代码如下: diff --git a/locales/zh/215-Interfaces/interfaces-2.md b/locales/zh/215-Interfaces/interfaces-2.md new file mode 100644 index 0000000..b522faa --- /dev/null +++ b/locales/zh/215-Interfaces/interfaces-2.md @@ -0,0 +1 @@ +现在来看一个完整的代码示例,了解接口的实际用法如下: diff --git a/locales/zh/216-Interface-Satisfaction/interface-satisfy-1.md b/locales/zh/216-Interface-Satisfaction/interface-satisfy-1.md new file mode 100644 index 0000000..d8d6ae0 --- /dev/null +++ b/locales/zh/216-Interface-Satisfaction/interface-satisfy-1.md @@ -0,0 +1,7 @@ +在 XGo 中,接口不需要显式实现——即没有 implement 关键字。相反,接口是隐式满足的。 +--- +一个类型如果拥有接口所要求的所有方法,就满足了该接口。例如,*os.File 满足 io.Reader、io.Writer、io.Closer 和 io.ReadWriter。*bytes.Buffer 满足 io.Reader、io.Writer 和 io.ReadWriter,但不满足 io.Closer,因为它没有 Close 方法。 +--- +接口的可赋值规则非常简单:只有当表达式的类型满足接口时,才能将其赋值给接口。即使右侧本身就是接口,该规则同样适用。例如: +--- +因为 io.ReadWriter 和 io.ReadWriteCloser 包含了 io.Writer 的所有方法,任何满足 io.ReadWriter 或 io.ReadWriteCloser 的类型也必然满足 io.Writer。 diff --git a/locales/zh/216-Interface-Satisfaction/interface-satisfy-2.md b/locales/zh/216-Interface-Satisfaction/interface-satisfy-2.md new file mode 100644 index 0000000..8924853 --- /dev/null +++ b/locales/zh/216-Interface-Satisfaction/interface-satisfy-2.md @@ -0,0 +1 @@ +就像信封包裹并隐藏了它所装的信件一样,接口包裹并隐藏了它所持有的具体类型和值。即使具体类型有其他方法,也只能调用接口类型所暴露的方法。例如: diff --git a/locales/zh/216-Interface-Satisfaction/interface-satisfy-3.md b/locales/zh/216-Interface-Satisfaction/interface-satisfy-3.md new file mode 100644 index 0000000..9316b28 --- /dev/null +++ b/locales/zh/216-Interface-Satisfaction/interface-satisfy-3.md @@ -0,0 +1,9 @@ +一个具体类型可以满足许多不相关的接口。考虑一个组织或销售数字化文化产品(如音乐、电影和书籍)的程序。它可能定义以下一组具体类型: +--- + Album + Book + Movie + Magazine + Podcast + TVEpisode + Track diff --git a/locales/zh/216-Interface-Satisfaction/interface-satisfy-4.md b/locales/zh/216-Interface-Satisfaction/interface-satisfy-4.md new file mode 100644 index 0000000..b7838a3 --- /dev/null +++ b/locales/zh/216-Interface-Satisfaction/interface-satisfy-4.md @@ -0,0 +1 @@ +我们可以将每个感兴趣的抽象表示为一个接口。某些属性对所有产品都是通用的,例如标题、创建日期和创作者列表(作者或艺术家)。 diff --git a/locales/zh/216-Interface-Satisfaction/interface-satisfy-5.md b/locales/zh/216-Interface-Satisfaction/interface-satisfy-5.md new file mode 100644 index 0000000..9778dbe --- /dev/null +++ b/locales/zh/216-Interface-Satisfaction/interface-satisfy-5.md @@ -0,0 +1 @@ +其他属性仅限于特定类型的产品。印刷文字的属性仅与书籍和杂志相关,而只有电影和电视剧集才有屏幕分辨率。 diff --git a/locales/zh/216-Interface-Satisfaction/interface-satisfy-6.md b/locales/zh/216-Interface-Satisfaction/interface-satisfy-6.md new file mode 100644 index 0000000..291be53 --- /dev/null +++ b/locales/zh/216-Interface-Satisfaction/interface-satisfy-6.md @@ -0,0 +1,3 @@ +这些接口只是将相关具体类型组合在一起并表达它们共同特征的一种有用方式。我们以后可能会发现其他分组方式。例如,如果我们发现需要以相同方式处理 Audio 和 Video 项目,可以定义一个 Streamer 接口来表示它们的共同特征,而无需更改任何现有的类型声明。 +--- +基于共享行为的具体类型分组可以表示为接口类型。与基于类的语言不同——在那些语言中,类所满足的接口集合是显式声明的——在 Go 中,我们可以在需要时定义新的抽象或感兴趣的分组,而无需修改具体类型的声明。当具体类型来自不同作者编写的包时,这一点尤其有用。当然,具体类型之间确实需要存在底层的共性。 diff --git a/locales/zh/217-Interface-Values/interface-values-1.md b/locales/zh/217-Interface-Values/interface-values-1.md new file mode 100644 index 0000000..c1a0df4 --- /dev/null +++ b/locales/zh/217-Interface-Values/interface-values-1.md @@ -0,0 +1,7 @@ +接口类型的值和接口值是两个不同的概念。 +--- +XGo 是一种静态类型编程语言,类型是编译时的概念,因此类型不是值。类型描述符的值提供了每个类型的信息,例如其名称和方法。在接口值中,类型组件由相应的类型描述符表示。 +--- +在 XGo 中,接口变量与其他类型变量一样,始终初始化为定义良好的值。接口的零值将其类型和值组件都设置为 nil,例如: +--- +接口变量 w 具有零值。因此它的动态类型和动态值都是 nil。在这种情况下,if w == nil 的结果为 true。接口变量 r2 具有非零值。它的动态类型是 *bytes.Reader,动态值是 nil。因此 r2 == nil 的结果为 false。 diff --git a/locales/zh/217-Interface-Values/interface-values-2.md b/locales/zh/217-Interface-Values/interface-values-2.md new file mode 100644 index 0000000..81acb58 --- /dev/null +++ b/locales/zh/217-Interface-Values/interface-values-2.md @@ -0,0 +1,5 @@ +接口值可以使用 == 和 != 进行比较。两个接口值相等的条件是:两者都为 nil,或者它们的动态类型相同且动态值根据该类型的 == 的通常行为相等。因为接口值是可比较的,所以可以用作 map 的键或 switch 语句的操作数。 +--- +但是,如果比较的两个接口值具有相同的动态类型,但该类型不可比较(例如 slice),则比较会导致 panic。例如 +--- +在这方面,接口类型是特殊的。其他类型要么可以安全比较(如基本类型和指针),要么完全不可比较(如 slice、map 和函数),但在比较接口值或包含接口值的聚合类型时,我们必须注意潜在的 panic 风险。将接口用作 map 键或 switch 操作数时也存在类似的风险。只有在确定接口值包含可比较类型的动态值时,才进行接口值的比较。 diff --git a/locales/zh/217-Interface-Values/interface-values-3.md b/locales/zh/217-Interface-Values/interface-values-3.md new file mode 100644 index 0000000..4f1f599 --- /dev/null +++ b/locales/zh/217-Interface-Values/interface-values-3.md @@ -0,0 +1,3 @@ +在处理错误或调试时,报告接口值的动态类型通常很有帮助。为此,我们使用 fmt 包的 %T 动词: +--- +在内部,fmt 使用反射来获取接口动态类型的名称。 diff --git a/locales/zh/218-The-error-Interface/error-1.md b/locales/zh/218-The-error-Interface/error-1.md new file mode 100644 index 0000000..1b0a2c5 --- /dev/null +++ b/locales/zh/218-The-error-Interface/error-1.md @@ -0,0 +1 @@ +error 接口有一个返回错误消息的方法: diff --git a/locales/zh/218-The-error-Interface/error-2.md b/locales/zh/218-The-error-Interface/error-2.md new file mode 100644 index 0000000..453ce43 --- /dev/null +++ b/locales/zh/218-The-error-Interface/error-2.md @@ -0,0 +1,3 @@ +创建错误最简单的方式是调用 errors.New,它为给定的错误消息返回一个新的错误。整个 errors 包只有四行代码: +--- +errorString 的底层类型是结构体而不是字符串,这是为了保护其表示不被意外(或故意)修改。而指针类型 *errorString(而非 errorString 本身)满足 error 接口的原因是,这样每次调用 New 都会分配一个不同的错误实例,与其他任何错误都不相等。 diff --git a/locales/zh/218-The-error-Interface/error-3.md b/locales/zh/218-The-error-Interface/error-3.md new file mode 100644 index 0000000..2cc4f9e --- /dev/null +++ b/locales/zh/218-The-error-Interface/error-3.md @@ -0,0 +1 @@ +我们不希望像 io.EOF 这样的特定错误与一个恰好具有相同消息的错误比较相等。 diff --git a/locales/zh/218-The-error-Interface/error-4.md b/locales/zh/218-The-error-Interface/error-4.md new file mode 100644 index 0000000..d972c8f --- /dev/null +++ b/locales/zh/218-The-error-Interface/error-4.md @@ -0,0 +1 @@ +直接调用 errors.New 的情况相对较少,因为有一个方便的包装函数 fmt.Errorf,它还能进行字符串格式化。 diff --git a/locales/zh/219-Type-Assertions/type-assert-1.md b/locales/zh/219-Type-Assertions/type-assert-1.md new file mode 100644 index 0000000..4d3f82b --- /dev/null +++ b/locales/zh/219-Type-Assertions/type-assert-1.md @@ -0,0 +1,3 @@ +类型断言是应用于接口值的操作。语法上,它看起来像 x.(T),其中 x 是接口类型的表达式,T 是一个类型,称为"断言"类型。类型断言检查其操作数的动态类型是否匹配断言的类型。 +--- +有两种可能。首先,如果断言类型 T 是具体类型,则类型断言检查 x 的动态类型是否与 T 相同。如果检查成功,类型断言的结果是 x 的动态值,其类型当然是 T。换句话说,对具体类型的类型断言从其操作数中提取具体值。如果检查失败,则操作会 panic。例如: diff --git a/locales/zh/219-Type-Assertions/type-assert-2.md b/locales/zh/219-Type-Assertions/type-assert-2.md new file mode 100644 index 0000000..70e03de --- /dev/null +++ b/locales/zh/219-Type-Assertions/type-assert-2.md @@ -0,0 +1 @@ +其次,如果断言类型 T 是接口类型,则类型断言检查 x 的动态类型是否满足 T。如果检查成功,动态值不会被提取;结果仍然是一个具有相同类型和值组件的接口值,但结果具有接口类型 T。换句话说,对接口类型的类型断言改变了表达式的类型,使得一组不同的(通常更大的)方法可以被访问,但它保留了接口值内部的动态类型和值组件。 diff --git a/locales/zh/219-Type-Assertions/type-assert-3.md b/locales/zh/219-Type-Assertions/type-assert-3.md new file mode 100644 index 0000000..e4bba1c --- /dev/null +++ b/locales/zh/219-Type-Assertions/type-assert-3.md @@ -0,0 +1 @@ +无论断言的是什么类型,如果操作数是 nil 接口值,类型断言都会失败。对限制更少的接口类型(方法更少的接口类型)进行类型断言很少需要,因为它的行为与赋值类似,除了在 nil 的情况下。 diff --git a/locales/zh/219-Type-Assertions/type-assert-4.md b/locales/zh/219-Type-Assertions/type-assert-4.md new file mode 100644 index 0000000..b817927 --- /dev/null +++ b/locales/zh/219-Type-Assertions/type-assert-4.md @@ -0,0 +1,3 @@ +通常我们不确定接口值的动态类型,我们想测试它是否是某个特定类型。如果类型断言出现在期望两个结果的赋值中,例如以下声明,操作在失败时不会 panic,而是返回一个额外的第二个结果——一个表示成功与否的布尔值: +--- +第二个结果通常赋值给名为 ok 的变量。如果操作失败,ok 为 false,第一个结果等于断言类型的零值,在本示例中是 nil *bytes.Buffer。 diff --git a/locales/zh/220-Type-Switches/type-switch.md b/locales/zh/220-Type-Switches/type-switch.md new file mode 100644 index 0000000..dd7d730 --- /dev/null +++ b/locales/zh/220-Type-Switches/type-switch.md @@ -0,0 +1 @@ +类型 `switch` 比较的是类型而不是值。你可以使用它来判断接口值的类型。在这个示例中,变量 `t` 将具有与其子句对应的类型。 diff --git a/main.go b/main.go index 263c888..955c103 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "encoding/json" "flag" "fmt" "log" @@ -12,31 +13,187 @@ import ( "strconv" "strings" "sync" - "sync/atomic" "text/template" - "unsafe" gohtml "html" + htmltemplate "html/template" "github.com/goplus/tutorial/internal" "github.com/russross/blackfriday/v2" ) const ( - chNumLen = 3 + chNumLen = 3 + defaultLang = "en" ) +// supportedLangs defines the non-default languages supported by the site. +var supportedLangs = map[string]bool{"zh": true, "ja": true, "ko": true} + +// langEntry represents a language option for the switcher UI. +type langEntry struct { + Code string + Name string +} + +// allLangs is the ordered list of all supported languages for template rendering. +var allLangs = []langEntry{ + {"en", "English"}, + {"zh", "中文"}, + {"ja", "日本語"}, + {"ko", "한국어"}, +} + +// translations holds locale strings keyed by language then by key. +var translations map[string]map[string]string + +// titleTranslations holds title translations keyed by language then by original title. +var titleTranslations map[string]map[string]string + +// loadTranslations reads all JSON files from the locales/ directory. +func loadTranslations(dir string) { + translations = make(map[string]map[string]string) + titleTranslations = make(map[string]map[string]string) + fis, err := os.ReadDir(dir) + if err != nil { + log.Printf("Warning: cannot read locales directory %s: %v", dir, err) + return + } + for _, fi := range fis { + if fi.IsDir() || !strings.HasSuffix(fi.Name(), ".json") { + continue + } + lang := strings.TrimSuffix(fi.Name(), ".json") + data, err := os.ReadFile(filepath.Join(dir, fi.Name())) + if err != nil { + log.Printf("Warning: cannot read locale file %s: %v", fi.Name(), err) + continue + } + var raw map[string]json.RawMessage + if err := json.Unmarshal(data, &raw); err != nil { + log.Printf("Warning: cannot parse locale file %s: %v", fi.Name(), err) + continue + } + m := make(map[string]string) + for k, v := range raw { + if k == "titles" { + titles := make(map[string]string) + if err := json.Unmarshal(v, &titles); err == nil { + titleTranslations[lang] = titles + } + continue + } + var s string + if err := json.Unmarshal(v, &s); err == nil { + m[k] = s + } + } + translations[lang] = m + } +} + +// lookup searches a nested translation map for the given language and key, +// falling back to the default language. +func lookup(dict map[string]map[string]string, lang, key string) (string, bool) { + if m, ok := dict[lang]; ok { + if v, ok := m[key]; ok { + return v, true + } + } + if lang != defaultLang { + if m, ok := dict[defaultLang]; ok { + if v, ok := m[key]; ok { + return v, true + } + } + } + return "", false +} + +// t returns the translated string for the given language and key. +func t(lang, key string) htmltemplate.HTML { + if v, ok := lookup(translations, lang, key); ok { + return htmltemplate.HTML(v) + } + return htmltemplate.HTML(key) +} + +// translateTitle returns the localized title for the given language. +func translateTitle(lang, title string) string { + if v, ok := lookup(titleTranslations, lang, title); ok { + return v + } + return title +} + +// extractLang parses the URL path to extract a language prefix. +// Returns (defaultLang, originalPath) for English, or (lang, cleanPath) for other languages. +func extractLang(urlPath string) (lang, cleanPath string) { + trimmed := strings.TrimPrefix(urlPath, "/") + parts := strings.SplitN(trimmed, "/", 2) + if len(parts) >= 1 && supportedLangs[parts[0]] { + lang = parts[0] + if len(parts) == 2 { + cleanPath = "/" + parts[1] + } else { + cleanPath = "/" + } + return + } + return defaultLang, urlPath +} + +// langPrefix returns the URL prefix for a language, e.g. "/zh" or "" for English. +func langPrefix(lang string) string { + if lang == defaultLang { + return "" + } + return "/" + lang +} + var ( headerTempl string footerTempl string + indexTmpl *template.Template exampleTmpl *template.Template ) +// templateFuncMap provides shared template functions including translation. +var templateFuncMap = template.FuncMap{ + "t": func(lang, key string) htmltemplate.HTML { + return t(lang, key) + }, + "langPrefix": langPrefix, + "translateTitle": translateTitle, + "allLangs": func() []langEntry { + return allLangs + }, + "currentLangName": func(code string) string { + for _, l := range allLangs { + if l.Code == code { + return l.Name + } + } + return code + }, +} + func init() { + loadTranslations("locales") + headerTempl = mustReadFile("templates/header.tmpl") footerTempl = mustReadFile("templates/footer.tmpl") - exampleTmpl = template.New("example") - _, err := exampleTmpl.Parse(headerTempl) + + indexTmpl = template.New("index").Funcs(templateFuncMap) + _, err := indexTmpl.Parse(headerTempl) + check(err) + _, err = indexTmpl.Parse(footerTempl) + check(err) + _, err = indexTmpl.Parse(mustReadFile("templates/index.tmpl")) + check(err) + + exampleTmpl = template.New("example").Funcs(templateFuncMap) + _, err = exampleTmpl.Parse(headerTempl) check(err) _, err = exampleTmpl.Parse(footerTempl) check(err) @@ -64,7 +221,7 @@ type exampleIndex struct { Title string Prev *exampleIndex Next *exampleIndex - cache *example + cache sync.Map // lang -> *example } type chapter struct { @@ -74,6 +231,7 @@ type chapter struct { var ( exampleIndexes map[string]*exampleIndex + tutorialNames []string watcher *internal.Watcher ) @@ -96,23 +254,21 @@ func listTutorial(dir string) (names []string, err error) { return } -func renderIndex(tutorial []string) []byte { - indexTmpl := template.New("index") - _, err := indexTmpl.Parse(headerTempl) - check(err) - _, err = indexTmpl.Parse(footerTempl) - check(err) - _, err = indexTmpl.Parse(mustReadFile("templates/index.tmpl")) - check(err) +// indexData wraps the data passed to the index template. +type indexData struct { + Lang string + Chapters []*chapter +} - var buf bytes.Buffer +// buildExampleIndexes builds the global exampleIndexes and chapter structure from tutorial names. +func buildExampleIndexes(tutorial []string) []*chapter { var indexes = make(map[string]*exampleIndex, len(tutorial)) var chs []*chapter var ch *chapter var prev *exampleIndex for _, name := range tutorial { title := name[chNumLen+1:] - titleEsc := strings.ReplaceAll(title, "-", " ") + titleEsc := gohtml.UnescapeString(strings.ReplaceAll(title, "-", " ")) if strings.HasSuffix(name[:chNumLen], "00") { ch = &chapter{Title: titleEsc} chs = append(chs, ch) @@ -136,7 +292,17 @@ func renderIndex(tutorial []string) []byte { prev = idx } exampleIndexes = indexes - err = indexTmpl.Execute(&buf, chs) + return chs +} + +// renderIndex renders the index page for the given language using pre-built chapters. +func renderIndex(chapters []*chapter, lang string) []byte { + data := &indexData{ + Lang: lang, + Chapters: chapters, + } + var buf bytes.Buffer + err := indexTmpl.Execute(&buf, data) check(err) return buf.Bytes() } @@ -251,6 +417,30 @@ func parseAndRenderSegs(sourcePath string) []*Seg { return segs } +// overrideDocsFromMD replaces the document segments in segs with content from a Markdown file. +// The Markdown file uses "---" separators to delimit sections that correspond to doc segments. +func overrideDocsFromMD(segs []*Seg, mdContent, mdPath string) { + sections := strings.Split(mdContent, "\n---\n") + docCount := 0 + for _, seg := range segs { + if seg.Docs != nil { + docCount++ + } + } + if len(sections) != docCount { + log.Printf("Warning: %s has %d sections but .gop has %d doc segments", mdPath, len(sections), docCount) + } + docIdx := 0 + for _, seg := range segs { + if seg.Docs != nil && docIdx < len(sections) { + text := strings.TrimSpace(sections[docIdx]) + seg.Docs = []string{text} + seg.DocsRendered = string(blackfriday.Run([]byte(text))) + docIdx++ + } + } +} + // ----------------------------------------------------------------------------- type exampleFile struct { @@ -264,6 +454,13 @@ type exampleFile struct { TailDoc []*Seg } +// exampleData wraps the example with language info for template rendering. +type exampleData struct { + *exampleIndex + Files []*exampleFile + Lang string +} + type example struct { *exampleIndex Files []*exampleFile @@ -297,42 +494,68 @@ func langOf(fname string) string { return fname[i+1:] } -func parseExample(dir string, idx *exampleIndex) *example { +// replaceExt replaces the file extension with a new one. +func replaceExt(fname, newExt string) string { + i := strings.LastIndex(fname, ".") + if i < 0 { + return fname + newExt + } + return fname[:i] + newExt +} + +func parseExample(dir string, idx *exampleIndex, lang string) *example { fis, err := os.ReadDir(dir) check(err) - example := &example{exampleIndex: idx} + ex := &example{exampleIndex: idx} for _, fi := range fis { fname := fi.Name() - lang := langOf(fname) - if lang != "xgo" && lang != "gop" { // only support XGo examples + fileLang := langOf(fname) + if fileLang != "xgo" && fileLang != "gop" { // only support XGo examples continue } sourcePath := filepath.Join(dir, fname) sourceSegs := parseAndRenderSegs(sourcePath) - if len(sourceSegs) != 0 { // ignore file with no segs - headDoc, code, tailDoc := classifySegs(sourceSegs) - file := &exampleFile{lang, headDoc, code, tailDoc} - example.Files = append(example.Files, file) + if len(sourceSegs) == 0 { // ignore file with no segs + continue + } + + // Try to load a language-specific .md override. + // sourceSegs are freshly parsed so we can mutate them directly. + if lang != defaultLang { + mdPath := filepath.Join("locales", lang, idx.Name, replaceExt(fname, ".md")) + if mdContent, err := os.ReadFile(mdPath); err == nil { + overrideDocsFromMD(sourceSegs, string(mdContent), mdPath) + } } + + headDoc, code, tailDoc := classifySegs(sourceSegs) + file := &exampleFile{fileLang, headDoc, code, tailDoc} + ex.Files = append(ex.Files, file) } - return example + return ex } -func renderExample(e *example) []byte { +func renderExample(e *example, lang string) []byte { + data := &exampleData{ + exampleIndex: e.exampleIndex, + Files: e.Files, + Lang: lang, + } var buf bytes.Buffer - err := exampleTmpl.Execute(&buf, e) + err := exampleTmpl.Execute(&buf, data) check(err) return buf.Bytes() } -func handleExample(w http.ResponseWriter, req *http.Request, root, path string) { - if idx, ok := exampleIndexes[path]; ok { - cache := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&idx.cache))) - if cache == nil { - cache = unsafe.Pointer(parseExample(filepath.Join(root, idx.Name), idx)) - atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&idx.cache)), cache) +func handleExample(w http.ResponseWriter, req *http.Request, root, urlPath, lang string) { + if idx, ok := exampleIndexes[urlPath]; ok { + cached, ok := idx.cache.Load(lang) + if !ok { + ex := parseExample(filepath.Join(root, idx.Name), idx, lang) + idx.cache.Store(lang, ex) + cached = ex } - data := renderExample((*example)(cache)) + data := renderExample(cached.(*example), lang) w.Write(data) return } @@ -342,7 +565,8 @@ func handleExample(w http.ResponseWriter, req *http.Request, root, path string) // ----------------------------------------------------------------------------- func handle(root string) func(w http.ResponseWriter, req *http.Request) { - var text []byte + var indexCache sync.Map // lang -> []byte + var chapters []*chapter var wg sync.WaitGroup wg.Add(1) go func() { @@ -350,13 +574,17 @@ func handle(root string) func(w http.ResponseWriter, req *http.Request) { if err != nil { log.Panicln(err) } - text = renderIndex(names) + tutorialNames = names + chapters = buildExampleIndexes(names) + enIndex := renderIndex(chapters, defaultLang) + indexCache.Store(defaultLang, enIndex) wg.Done() watcher, err = internal.NewWatcher(func(string) { for _, v := range exampleIndexes { - atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&v.cache)), nil) + v.cache = sync.Map{} } + indexCache = sync.Map{} }) if err != nil { log.Panicln(err) @@ -371,16 +599,25 @@ func handle(root string) func(w http.ResponseWriter, req *http.Request) { if !path.IsAbs(urlPath) { urlPath = "/404.html" } - if urlPath == "/" { + + lang, cleanPath := extractLang(urlPath) + + if cleanPath == "/" { wg.Wait() - w.Write(text) + cached, ok := indexCache.Load(lang) + if !ok { + text := renderIndex(chapters, lang) + indexCache.Store(lang, text) + cached = text + } + w.Write(cached.([]byte)) return } - if path.Ext(urlPath) != "" { - http.ServeFile(w, req, "./public"+urlPath) + if path.Ext(cleanPath) != "" { + http.ServeFile(w, req, "./public"+cleanPath) return } - handleExample(w, req, root, urlPath) + handleExample(w, req, root, cleanPath, lang) } } diff --git a/public/site.css b/public/site.css index 163a778..88393ec 100644 --- a/public/site.css +++ b/public/site.css @@ -79,19 +79,89 @@ body { } } -/* breadcrumb */ -.breadcrumb { +/* top bar: breadcrumb + language switcher */ +.top-bar { display: flex; - flex-direction: row; + justify-content: space-between; + align-items: center; margin-top: 36px; - font-size: 16px; - line-height: 1.5; } @media screen and (max-width: 640px) { - .breadcrumb { + .top-bar { margin-top: 0; } } + +/* language switcher dropdown */ +.lang-switcher { + position: relative; +} +.lang-switcher-btn { + display: flex; + align-items: center; + gap: 4px; + padding: 0; + border: none; + background: none; + color: #999; + font-size: 16px; + line-height: 1.5; + cursor: pointer; + transition: color .3s; + font-family: inherit; +} +.lang-switcher-btn:hover { + color: #333; +} +.lang-icon { + flex-shrink: 0; +} +.lang-arrow { + flex-shrink: 0; + transition: transform .2s; +} +.lang-switcher.open .lang-arrow { + transform: rotate(180deg); +} +.lang-menu { + display: none; + position: absolute; + top: calc(100% + 6px); + right: 0; + min-width: 100px; + padding: 4px 0; + background: #fff; + border: 1px solid #e5e5e5; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); + z-index: 100; +} +.lang-switcher.open .lang-menu { + display: block; +} +.lang-menu-item { + display: block; + padding: 6px 14px; + color: #333; + font-size: 14px; + text-decoration: none; + transition: background-color .15s; +} +.lang-menu-item:hover { + background-color: #f5f5f5; +} +.lang-menu-item.active { + color: #2C84FF; + font-weight: 500; +} + +/* breadcrumb */ +.breadcrumb { + display: flex; + flex-direction: row; + font-size: 16px; + line-height: 1.5; +} .breadcrumb-link-item, .breadcrumb-sep { color: #999999; } diff --git a/templates/example.tmpl b/templates/example.tmpl index 8c3e15f..a23f023 100644 --- a/templates/example.tmpl +++ b/templates/example.tmpl @@ -1,8 +1,8 @@ - + - XGo by Tutorials: {{.Title}} + {{t .Lang "site_title"}}: {{translateTitle .Lang .Title}} @@ -11,25 +11,39 @@ {{ template "header" }}
- +
+ +
+ +
+ {{range allLangs}} + {{.Name}} + {{end}} +
+
+
-

{{.Title}}

+

{{translateTitle .Lang .Title}}

{{if .Files}}{{else}} -

No content yet, you can help us build it here.

+

{{t .Lang "no_content_before"}}{{t .Lang "no_content_link"}}{{t .Lang "no_content_after"}}

{{end}} {{range .Files}} {{if .HeadDoc}} @@ -52,7 +66,7 @@ {{end}} {{if .Next}}

- Next example: {{.Next.Title}} + {{t .Lang "next_example"}} {{translateTitle .Lang .Next.Title}}

{{end}}
@@ -61,6 +75,13 @@ diff --git a/templates/index.tmpl b/templates/index.tmpl index e114a2a..a5aab1e 100644 --- a/templates/index.tmpl +++ b/templates/index.tmpl @@ -1,8 +1,8 @@ - + - XGo by Tutorials + {{t .Lang "site_title"}} @@ -11,31 +11,51 @@ {{ template "header" }}
- +
+ +
+ +
+ {{range allLangs}} + {{.Name}} + {{end}} +
+
+
-

Tutorials

+

{{t .Lang "index_heading"}}

- XGo is an open source programming language aimed to enable everyone to become a builder of the world. + {{t .Lang "index_desc_1"}}

- XGo by Tutorials is a hands-on introduction - to XGo using annotated example programs. Check out - the first example or - browse the full list below. + {{t .Lang "index_desc_2"}}

- {{range .}} - {{if .Title}}{{end}} + {{range .Chapters}} + {{if .Title}}{{end}} {{end}}
{{ template "footer" }} +