Culebraクイックガイド ========================== 引きようがないものを1ファイルにまとめたもの: 構文、他言語から **持ち込むと外れる**習慣、そして「使おうと言われなくても手が伸びる」 範囲のライブラリのシグネチャ。凝縮元のリファレンス ([`handbook.ja.md`](handbook.ja.md)、[`language.ja.md`](language.ja.md)、 [`stdlib.ja.md`](stdlib.ja.md)) は合計15万トークン規模で、これは そのうちプロンプトに載る部分です。 ここに載せていないもの — 3D、2D、ソケット、パーサ、全文検索 — は §4で名前だけを挙げ、その章をまるごと出すコマンドを添えてあります。 署名の一覧だけでは動かし方が分からず、章を読めば分かるからです。 以下の` ```culebra `ブロックはすべて`culebra test --doc docs`が 実行するので、実装から乖離することはありません。行末の `# => <値>`は検証済みのstdout、`# !! <パターン>`は検証済みのthrow です。§4は`just gen-quick-guide`がリファレンスから生成します — 手で編集しないでください。 目次 ---- 1. [プログラムを実行する](#1-プログラムを実行する) 2. [構文](#2-構文) 3. [持ち込むと外れる習慣](#3-持ち込むと外れる習慣) 4. [シグネチャ索引](#4-シグネチャ索引) 5. [テンプレート](#5-テンプレート) ## 1. プログラムを実行する ```bash culebra prog.cul # バイトコードVM (既定) culebra --jit prog.cul # LLVM ORC JIT — 出力は同一 culebra build prog.cul -o prog # AOT、自己完結バイナリ culebra test # cwd以下のtest_*.culを全実行 culebra fmt -i . # その場で整形 (スタイル指定は無し) culebra lint . # 静的検査; 警告1 / エラー2でexit culebra docs -g 'Math.wrap' # リファレンスから署名を引く culebra docs stdlib Scene # 名前空間の章をまるごと出す ``` ソースファイルの拡張子は`.cul`。プロジェクトファイルもマニフェストも パッケージマネージャもありません。標準ライブラリは全部`import`なしで スコープに入っています。 未定義の名前はプログラム実行**前**に弾かれるので、ライブラリ名の当て 推量は途中まで動くことなく即座に失敗します: ```culebra # !! undefined variable 'puts' puts('hi') ``` 弾かれるのは名前であってメンバではありません。`Math.abss(1)`や `xs.len()`は`lint`を素通りし、その行が実行されて初めて落ちます。 存在しないプロパティはエラーでなく`nil`です。書いたら実行すること — lintを通しただけのプログラムは検査されていません。 リファレンス一式はバイナリの中にあり、実行中のビルドと常に一致します。 `culebra docs -g <パターン>`は一致したセクションを表示し、無ければ exit 1になるので、出力を読まずにAPIの実在を判定できます。パターン は識別子か語句であって、質問文ではありません。 ## 2. 構文 ### 2.1 束縛 束縛は`mut`を付けない限り不変です。裸の`x = ...`は新規束縛を作るか、 最も近い外側の束縛を再代入します。 ```culebra x = 1 # 新規不変束縛、または外側を再代入 let y = 2 # 不変; 外側のシャドウは不可 mut z = 3 # 可変 z = 4 # bare再代入 z += 1 # -= *= /= %= **= @= &= |= ^= <<= >>= も同様 inspect(z) # => 5 ``` 引数も不変です。代入するとローカルコピーではなくエラーになります: ```culebra bump = fn (n) { n += 1 n } inspect(try { bump(1) } catch e { e.kind }) # => 'ImmutableError' ``` **キャプチャした外側**の変数をシャドウする束縛の導入はコンパイル時 エラーです。1つの関数内でのブロックローカルな再束縛は問題ありません。 ### 2.2 型 ```culebra inspect(type_of(nil)) # => 'Nil' inspect(type_of(true)) # => 'Bool' inspect(type_of(42)) # => 'Long' inspect(type_of(3.14)) # => 'Float' inspect(type_of('hi')) # => 'String' inspect(type_of([1, 2])) # => 'Array' inspect(type_of({a: 1})) # => 'Object' inspect(type_of(fn () { 1 })) # => 'Function' inspect(type_of((1, 'a'))) # => 'Tuple' inspect(type_of(Set(1, 2))) # => 'Set' inspect(type_of('hello'.slice(1, 3))) # => 'StringView' ``` 12番目が`Tensor`です (§4)。クラス・モジュール・エラーはいずれも `Object`の上に構築されています。 ### 2.3 制御フロー `if` / `match` / `cond`は式、`while` / `for`は文です。 ```culebra n = 7 sign = if n > 0 { 1 } else if n < 0 { -1 } else { 0 } inspect(sign) # => 1 label = match n { 0 => 'zero', k if k < 10 => 'small', _ => 'large', } inspect(label) # => 'small' grade = cond { # 主語のないmatch n >= 90 => 'A', n >= 5 => 'B', _ => 'C', } inspect(grade) # => 'B' inspect(n > 5 ? 'big' : 'small') # => 'big' ``` ```culebra for i in 0..3 { inspect(i) } # 半開; 0..=2は閉区間; `by k`で刻む # => | # 0 # 1 # 2 for k, v in {a: 1, b: 2} { inspect("{k}={v}") } # => | # 'a=1' # 'b=2' ``` `break` / `continue`は両方のループで使えます。どちらのループにも ラベルを付けられ、`break` / `continue`でそのラベルを指定すると、最内 ループではなくそのループを抜けます(次のiterationへ進みます)。ラベルは キーワードと同じ行に置き、変数名は取りません。`nobreak`ブロックは ループが`break`されなかったときだけ走ります。`while` / `if` / `match`は構文内に閉じたinit節を取れます: ```culebra for v in [1, 3, 5] { if v % 2 == 0 { break } } nobreak { inspect('all odd') # => 'all odd' } while mut i = 0; i < 3 { i += 1 } if let m = 6; m > 5 { inspect('big') } # => 'big' search: for row in [[1, 2], [3, 4]] { for cell in row { if cell == 3 { inspect(cell) # => 3 break search } } } ``` ### 2.4 関数 ```culebra add = fn (a, b) { a + b } inspect(add(2, 3)) # => 5 typed = fn (a: Long, b: Long) -> Long { a + b } inspect(typed(2, 3)) # => 5 square = |x| x * x # lambdaのbodyは既定で単一式 inspect(square(7)) # => 49 inspect([1, 2, 3].map(|x| x * 2)) # => [2, 4, 6] inspect([[1, 2]].map(fn ((a, b)) { a + b })) # => [3] ``` 値としてリテラルを束縛する (`name = fn (...) { ... }`) か、宣言形式 `fn name(...) { ... }`を使うかのどちらか。その場で渡すcallbackは `|x|`。波括弧も使え、`|x| { ... }`は`fn (x) { ... }`と同じく文を 受け付ける。 **名前を付けて呼ぶ関数**、特に再帰関数には宣言形式を使うこと。 [ジェネレータ](#26-イテレータ)になれる、[多重ディスパッチ](#29-クラスufcs多重ディスパッチtrait) に参加できる、`fn.name`にソースレベルの名前が入るのはいずれも宣言形式 だけだが、それ以外の点では2つの形式は等価で、どちらのbodyでも裸の `fn`はその関数自身を呼ぶ: ```culebra fn fib(x) { if x < 2 { x } else { fn(x - 2) + fn(x - 1) } } inspect(fib(10)) # => 55 inspect(fib.name) # => 'fib' ``` ブロックは最後の式に評価されるので`return`はほとんど不要です。`*` 以降はキーワード専用、`**rest`は未知のキーワードを、`*rest`は余った 位置引数を集めます: ```culebra greet = fn (name, *, greeting = 'hi', **opts) { suffix = opts.has('loud') && opts.loud ? '!' : '' "{greeting}, {name}{suffix}" } inspect(greet('alice')) # => 'hi, alice' inspect(greet('bob', greeting: 'yo')) # => 'yo, bob' inspect(greet('cy', loud: true)) # => 'hi, cy!' inspect(greet('dee', **{greeting: 'hey'})) # => 'hey, dee' ``` ### 2.5 文字列 補間されるのは二重引用符だけ。単一引用符はリテラルです。 ```culebra name = 'Culebra' inspect("hello, {name}") # => 'hello, Culebra' inspect('hello, {name}') # => 'hello, {name}' inspect('a' + 'b') # => 'ab' ``` `size()`はUTF-8のバイト数、`for`と`iter()`はUnicodeスカラー 1個ずつ、`graphemes()`は書記素クラスタ1個ずつ進みます。`s[i]` 演算子は**ありません** — バイトオフセットを取る`slice`を使います。 ```culebra inspect('café'.size()) # => 5 inspect('café'.graphemes().collect().size()) # => 4 inspect('café'.slice(0, 1)) # => 'c' inspect('hello world'.split(' ')) # => ['hello', 'world'] inspect(['a', 'b'].join('-')) # => 'a-b' ``` `"""`はブロック文字列。`"..."`と同じく補間し、閉じ区切りのインデントを 取り除き、その直前の改行も落とす。閉じ`"""`は独立した行に置く。複数行の 文字列は`\n`を連結せずこれを使う。 ```culebra sql = """ SELECT * FROM t """ inspect(sql.lines()) # => ['SELECT *', 'FROM t'] ``` ### 2.6 イテレータ `range`は遅延、`iota`は確保します。`.iter()`はArrayを遅延化し、 チェーンは最初のconsumerで止まって中間Arrayを作りません。 ```culebra inspect(iota(3)) # => [0, 1, 2] inspect(range(1000).filter(|x| x % 2 == 0).map(|x| x * 3).take(4).collect()) # => [0, 6, 12, 18] inspect(range(1, 11).reduce(0, |a, x| a + x)) # => 55 inspect([1, 2, 3, 4].iter().zip(['a', 'b']).collect().size()) # => 2 for i, v in ['x', 'y'].enumerate() { inspect("{i}:{v}") } # => | # '0:x' # '1:y' ``` bodyに`yield`を含む関数はジェネレータになり、呼ぶとイテレータが 返ります。 ```culebra fn countdown(start) { mut i = start while i > 0 { yield i i -= 1 } } inspect(countdown(3).collect()) # => [3, 2, 1] ``` yieldするメソッドを書けば、そのオブジェクトは反復可能になります (`iter() { for x in self.items { yield x } }`)。`iter()` / `has_next()` / `next()`を持つオブジェクトなら何でも`for`と全チェーンメソッドで使えます。 ### 2.7 パターンマッチ ```culebra describe = fn (v) { match v { 0 => 'zero', 1 | 2 | 3 => 'small', ..0 => 'negative', n: Long if n > 100 => "big ({n})", n: Long => "int ({n})", s: String => "str ({s})", [] => 'empty', [x] => "one ({x})", [head, ...tail] => "head={head} rest={tail.size()}", {name} => "named {name}", _ => 'other', } } inspect(describe(2)) # => 'small' inspect(describe(-2.5)) # => 'negative' inspect(describe(999)) # => 'big (999)' inspect(describe([1, 2, 3])) # => 'head=1 rest=2' inspect(describe({name: 'z'})) # => 'named z' ``` 範囲パターン(`..0`、`1..=9`、`0.5..`)は、その区間に入る数値なら`Long`でも `Float`でも一致します(`(1..=9).contains(x)`が受け付ける値と同じ)。 網羅性検査はありません。`_`の腕を用意してください。 裸の名前は先頭の1文字で読み分けます。大文字なら型の検査で、何も束縛 しません(`String => …`、`Shape.Origin => …`)。それ以外の名前は値を束縛 します。定数と比べるにはガードを使います(`n if n == LIMIT`)。 同じパターンで束縛できます(`let (a, b) = pair`)。`let`を外すと**既存の 変数**への代入になり、右辺を先に全部評価します — `(a, b) = (b, a)`で 入れ替え、`(x, y) = match … { … }`で2つ同時に書き込めます。 ### 2.8 エラー・`defer`・`drop` throwできる値に制限はなく、`try`は式です。 ```culebra check = fn (x) { if x < 0 { throw "negative: {x}" } x } inspect(try { check(-1) } catch e { e }) # => 'negative: -1' inspect(try { check(7) } catch _ { 0 }) # => 7 ``` `catch`の後ろは`match`の腕と同じパターンで、`try`には節をいくつも 書けます。`catch e`は投げられた値をすべて受け、検査をする節は一致した値 だけを受け、どの節も受けなかった値はそのまま伝播します。 ```culebra class Timeout { new(.after) {} } wait = fn (v) { try { throw v } catch t: Timeout if t.after > 10 { 'slow' } catch Timeout { 'timeout' } catch IOError { 'io' } catch e { "other: {e}" } } inspect(wait(Timeout(30))) # => 'slow' inspect(wait(Timeout(1))) # => 'timeout' inspect(wait('x')) # => 'other: x' ``` 組み込みエラーは`kind`を持つObjectで、kindはそのkindを持つObjectを受ける 型名でもあります(`catch IOError`、`e: TypeError`): ```culebra inspect(try { 1 / 0 } catch e { e.kind }) # => 'ZeroDivisionError' ``` `defer`は囲むブロックのあらゆる脱出経路でLIFO順に走ります。引数 なしの`drop`プロパティを持つObjectは、最後の参照が消えた時点で それが呼ばれます。 ```culebra { defer { inspect('second') } defer { inspect('first') } inspect('body') } # => | # 'body' # 'first' # 'second' ``` ```culebra { r = {drop: fn () { inspect('released') }} inspect('in scope') } inspect('after') # => | # 'in scope' # 'released' # 'after' ``` ### 2.9 クラス・UFCS・多重ディスパッチ・trait フィールドには`self`でアクセスします (`this`ではありません)。 クラス自体の呼び出しは`.new`の短縮形です。クラスもObjectリテラルと 同じ規則で`drop()`を定義でき (2.8)、インスタンスへの最後の参照が 消えた時点でそれが呼ばれます。 ```culebra class Car { wheels = 4 # デフォルト付きの宣言フィールド new(.mpr, .wheels?) { # field parameter: self.mpr / self.wheelsにも格納 self.miles = 0 } drop() {} # 最後の参照が消えた時点でリソースを後始末 run(n) { self.miles += self.mpr * n } get far() { self.miles > 10 } # 計算プロパティ、括弧なしで呼ぶ static unit() { Car(1) } } c = Car(5) c.run(3) inspect(c.miles) # => 15 inspect(c.far) # => true inspect(c.wheels) # => 4 inspect(type_of(c)) # => 'Car' ``` `new`のパラメータを`.x`と書くとフィールド`x`にも格納します(本体に 宣言がなければそのフィールドを宣言します)。`.x?`は省略可能で宣言の デフォルトを取るので、`Car(5, wheels: 3)`は4を上書きします。 演算子はdunderメソッド (`__add__`、`__eq__`、`__lt__`、`__index__`、 `__setindex__`、`__call__`等) に対応します。逆側メソッド (`__radd__`) はありません — その演算を所有する型の側にオーバーロード を置いてください。 自由関数`f(x, ...)`は`x.f(...)`として呼べますが、既存のプロパティ やメソッドが常に優先されます。また、第一引数の型注釈が`x`を 受け取れない`f`は候補になりません: ```culebra double = fn (x) { x * 2 } inspect(42.double()) # => 84 ``` 同名の自由関数を複数定義すると、宣言された引数型でディスパッチします: ```culebra class Circle { new(r) { self.r = r } } fn area(c: Circle) { 3 * c.r * c.r } fn area(n: Long) { n } inspect(area(Circle(2))) # => 12 inspect(area(10)) # => 10 ``` `trait`は構造的です。メソッド名とアリティが一致するクラスは`impl` 宣言なしで適合します。traitメソッドはデフォルト実装のbodyを持て、 `@derive(Eq, Hash, Show, Comparable)`が定型の適合メソッドを生成します。 ```culebra trait Greeter { hello() -> String } class Bob { new(n) { self.n = n } hello() { "hi, {self.n}" } } greet = fn (g: Greeter) -> String { g.hello() } inspect(greet(Bob('Ann'))) # => 'hi, Ann' ``` `enum`はsum typeです。nullary variantはsingleton値、payload variantは コンストラクタで、`match`で分解します。variantはそのまま`Eq`かつ `Hashable`なので、Object / Setのkeyになります。 ```culebra enum Shape { Circle(Float), Rect(Float, Float), Origin, } area = fn (s) { match s { Circle(r) => 3 * r * r, Rect(w, h) => w * h, Origin => 0, } } inspect(area(Shape.Rect(2.0, 3.0))) # => 6.0 mut seen = {} seen[Shape.Origin] = 'origin' inspect(seen[Shape.Origin]) # => 'origin' inspect(Set(Shape.Origin, Shape.Origin).size()) # => 1 ``` ### 2.10 エフェクト `perform`は、意味を外側の`handle`が決める操作を発行します。継続は multi-shotです。 ```culebra effect fn ask() inspect(handle { perform ask() * 2 } with ask(resume) { resume(21) }) # => 42 ``` `resume`を呼ばない節は残りの計算を捨てます — これはまさに例外です。 `with return(v) { ... }`は正常完了時の値を写します。 ### 2.11 モジュール `export` / `import`はトップレベル専用なので、依存グラフはparse時に 確定します。 ```culebra # lib.cul let greet = fn (n) { "hello, {n}" } export {greet} ``` ```culebra # doctest: skip # main.cul import lib from './lib.cul' inspect(lib.greet('world')) # => 'hello, world' ``` パスはimport元ファイルからの相対で解決される単一引用符リテラルです。 各モジュールの評価は1回だけ。循環は`ImportError`になります。 ### 2.12 オプショナル型注釈 注釈が検査されるのは代入・引数渡し・戻り値の3境界だけで、それ以外 では検査されません。`Long | String`はUnion、`T?`は`T | Nil`、 `Array`は要素型を**文書化**しますが要素ごとの検査はしません。 ```culebra show = fn (x: Long | String) -> String { to_string(x) } inspect(show(1)) # => '1' inspect(show('hi')) # => 'hi' ``` ## 3. 持ち込むと外れる習慣 各行は、他言語の習慣のまま書くと失敗するか、黙って別のものになる ケースです。 | つい書くもの | Culebraでは | |---|---| | `'text {x}'`の補間 | 補間されるのは`"..."`のみ。`'...'`はリテラル | | `puts` / `console.log` / 改行付きの`print(x)` | `inspect(x)`はクォート付きデバッグ形式、`println(x)`は生+改行、`print(x)`は生 | | `this` | `self` | | `x \|> f()` | UFCSの`x.f()`。パイプライン演算子は無い | | Setとして`{1, 2}`・`{3}` | 波括弧はObjectのもの。Setは`Set(1, 2)`・`Set(x)`・`Set()` | | 集合の`a \| b` / `a & b` | メソッド: `a.union(b)` / `a.intersect(b)` / `a.diff(b)` | | `{a: 1} + {b: 2}` | `TypeError`。Objectのマージ演算子は無い | | Stringへの`s[0]` | `TypeError`。バイトオフセットを取る`s.slice(0, 1)`を使う | | `-7 % 3 == 2` (Python) | `-1` — 符号は被除数に従う (C流) | | `-7 / 2 == -4` (Python) | `-3` — Long除算はゼロ方向に切り捨て | | `if c { x = 1 } else { x = 2 }`の後で`x`を読む | 読込時の`NameError`。各腕は独自のスコープ。`x = if c { 1 } else { 2 }`と書く | | `if [] { }` / `if '' { }` | `TypeError`。判定できるのは`Bool` / `Long` / `Float`のみ | | `0 == false` | `false` — 型をまたぐ暗黙変換は無い | | `.length` / `.count` | `.size()`。存在しないプロパティは`nil`なので`.length`はraiseせず`nil`になる | | `.append(x)` | `.push(x)` | | `xs.sort(\|a, b\| ...)` — 比較関数を渡す | `xs.sorted_by(key)`が取るのは比較関数ではなく**キー関数**。降順は`reverse: true`。Tupleのキーなら複数の項目で並ぶ: `xs.sorted_by(\|x\| (x.a, x.b))`。`<=>`は無い | | `xs.take(n)` / `xs[:n]` | `xs.slice(0, n)`。`take`はIteratorのメソッド — `xs.iter().take(n).collect()` | | `obj.items()` / `obj.entries()` / `obj.to_array()` | `for k, v in obj`、または`obj.keys().map(\|k\| (k, obj[k]))` | | `del a[i]` / `a.splice(i, 1)` | `a.remove_at(i)`。取り除いた要素を返す | | `obj['missing']` | `KeyError`。`obj.missing`は`nil`、`obj.get('missing', dflt)`はfallbackを取る | | `'ab' * 3` | `TypeError`。文字列の繰り返し演算子は無い | | `s.find(x)` / `s.indexOf(x)` | `s.index_of(x)` — バイトオフセット、無ければ`-1` | | `s.ljust(10)` / `s.padStart(10)` / `s.zfill(5)` | 補間の書式指定: `"{s:<10}"`、`"{n:05}"` | | 引数なしの`s.split()`(空白で分割) | `s.split_whitespace()`。`split`は必ず区切りを取る | | 引数への代入 | 引数は不変 — `ImmutableError` | | 同一スコープで`x = 1`を2回 | `ImmutableError`。`mut x = 1`と宣言する | | `and` / `or` / `not` | `&&` / `\|\|` / `!` | | `elif` | `else if` | | コメントが`#`だけ、または`//`だけ | 両方使える。加えて`/* ... */` | | `async` / `await` | 設計上存在しない — I/Oはblocking。`Isolate` / `Parallel`を使う | | パッケージマネージャ | 標準ライブラリは全部`import`なしでスコープにある | エラーにならず値が返るぶん見落としやすいものが3つ: ```culebra # split が返すのは String ではなく StringView。安いが type_of は異なる inspect(type_of('a,b'.split(',')[0])) # => 'StringView' # 存在しないプロパティは黙って nil inspect([1, 2].length) # => nil inspect([1, 2].size()) # => 2 # どの arm にも当たらない match はエラーではなく nil。lint が指摘するのは # 対象が enum のときだけなので、それ以外の match には `_` の arm を置く inspect(match 9 { 0 => 'zero', }) # => nil ``` ### 慣用形 上の表はエラーになるもの。ここに挙げるのは**動く**が、この言語の書き方では ないもので、何も警告してくれない。右の列で書く。 他言語からの移植では、`if/elif/else`や`case`/`switch`の連鎖はここでは `match`/`cond`に対応する。`if`/`else if`の連鎖としてそのまま持ち込むのは、 移植でもっとも起こりやすい取り違えである。 | 動くが | こう書く | |---|---| | 値としての`if c { a } else { b }` | `c ? a : b` | | 値を返す`if` / `else if`の連鎖 | 1つの値を定数群と比較するなら`match`、互いに無関係な条件なら`cond` | | `ClassName.new(...)` | `ClassName(...)` — `class`定義型は呼び出し可能で、コンストラクタ呼び出しの糖衣になる | | `new(x, y) { self.x = x; self.y = y }` | `new(.x, .y) {}` — field parameterはフィールドにも格納する | | `"a" + x + "b"`(値の前後にリテラル文字列を継ぎ足す) | `"a{x}b"` | | `i = i + 1` | `i += 1` | | `x.size() == 0` / `> 0` | `x.empty()` / `!x.empty()` | | ループの外に`mut i = 0`を置いて`i += 1` | `for i, v in xs.enumerate()` | | `mut out = []` + `for` + `out.push(f(x))` | `xs.map(f)`(`filter`も同様) | | `mut t = {}` + `for` + `t[k] = v` | `xs.map(\|x\| (k(x), v(x))).to_object()` | | `mut found = false` + `while !found` | `for x in xs { … break }`か`xs.find(p)` | | `mut hit = false` + `for` + `if p(x) { hit = true; break }` | `xs.any(p)`(逆は`xs.all(p)`) | | `"a\n" + "b\n"` | `"""`ブロック | | `.map(fn (x) { expr })` | `.map(\|x\| expr)` | | `range(0, n)` | `range(n)` | | `iota(n).map(\|_\| v)` | `repeat(n, v)` | | `for i in 0..xs.size() { xs[i] … }` | `for x in xs` | | `mut i = start; while i < end { …; i += 1 }` | `for i in start..end { … }` | | `match n { k if k < 10 => … }` / `x >= lo && x < hi` | `match n { ..10 => … }`(範囲パターン)。判定だけなら`(lo..hi).contains(x)` | | `{k1: v1, k2: obj.k2, k3: obj.k3}`(1つ変えるために全フィールドを手コピー) | `{...obj, k1: v1}` | | `if cond { stmt }` — `else`なしの単文 | `stmt if cond`(否定は`stmt unless cond`) | | `if x == nil { x = v }` / `if !d.has(k) { d[k] = v }` | `x ??= v` / `d[k] ??= v`(`obj.key`にも使える) | | `"{a.b(c).d ?? e}"`(長く入れ子になった式をそのまま埋め込む) | `let x = a.b(c).d ?? e`としてから`"{x}"` | `cond`は主語のない`match`なので、互いに無関係な条件の連鎖は `cond { a > 1 => …, b < 2 => …, _ => … }`になる。完走したかどうかを 知りたいループはフラグでなく`nobreak`ブロックを使う。 これはCulebraで書かれた型に限らず、ライブラリが載せているコンストラクタ すべてに当てはまる。`Scene.Image(4, 4)`は`Scene.Image.new(4, 4)`と、 `Channel()`は`Channel.new()`と同じ呼び出しになる。§4の索引が`.new(...)`と 表記しているのは、説明対象がそのメソッド自体だからである。 ## 4. シグネチャ索引 レシーバ名は慣例です: `s`はString、`a`はArray、`o`はObject、 `it`はIterator、`re`はコンパイル済みRegex、`f`は開いたFile。 レシーバが付いていないエントリ (**セットのメソッド** の`contains(x)`、 **Http** の`json()`等) は、そのグループ自身の型のメソッドです。 **(experimental)**が付いたグループはビルド時のopt-inで、リリースバイナリ には入っていないことがあります([`stdlib.ja.md`](stdlib.ja.md)の該当節に 書いてあります)。 末尾の表にある名前空間は、一覧にせず名前だけを挙げてあります。どれも状態を 持つ仕組み — viewとフレームループ、開いた接続、文法、エミッタ — を 動かすもので、署名は「何を呼ぶか」は言っても「どの順で、何に対して、 結果は誰のものか」を言いません。`culebra docs stdlib <名前>`でその章が まるごと(前書きも小節も込みで)出ます。その名前空間を使う前に読んで ください。署名から推測して書くと、lintは通るのに動かないコードになり ます。 **文字列メソッド** — s.size() -> Long; s.empty() -> Bool; s.presence() -> String | StringView | Nil; s.upper() -> String; s.lower() -> String; s.capitalize() -> String; s.title() -> String; s.normalize(form: StringLike = "NFC") -> String; s.eq_ignore_case(other: StringLike) -> Bool; s.reverse() -> String; s.repeat(n: Long) -> String; s.truncate(max: Long, ellipsis: StringLike = "...") -> String; s.trim() -> String; s.trim_start(chars: StringLike = "") -> String; s.trim_end(chars: StringLike = "") -> String; s.tr(from: StringLike, to: StringLike) -> String; s.split(sep: StringLike, limit: Long = 0) -> Array; s.rsplit(sep: StringLike, limit: Long = 0) -> Array; s.split_whitespace() -> Array; s.split_once(sep: StringLike) -> Tuple | Nil; s.rsplit_once(sep: StringLike) -> Tuple | Nil; s.split_iter(sep: StringLike) -> Iterator; s.lines() -> Array; s.replace(pat: String | Regex, repl: String | Function) -> String; s.contains(sub: StringLike) -> Bool; s.count(sub: StringLike) -> Long; s.starts_with(prefix: StringLike) -> Bool; s.ends_with(suffix: StringLike) -> Bool; s.index_of(sub: StringLike, start: Long = 0) -> Long; s.last_index_of(sub: StringLike) -> Long; s.strip_prefix(prefix: StringLike) -> String; s.strip_suffix(suffix: StringLike) -> String; s.replace_first(pat: String | Regex, repl: String | Function) -> String; s.is_digit() -> Bool; s.is_alpha() -> Bool; s.is_alnum() -> Bool; s.is_space() -> Bool; s.is_ascii() -> Bool; s.slice(start: Long, end: Long) -> StringView; s.view() -> StringView; s.to_string() -> String; s.iter() -> Iterator; s.code_points() -> Iterator; s.graphemes() -> Iterator; s.words() -> Iterator; s.sentences() -> Iterator; s.bytes() -> Iterator; String.from_code_point(cp: Long) -> String; String.from_code_points(cps: Array) -> String; String.from_bytes(bytes: Array) -> String; String.pack(type: String | Class, values: Array, endian: String = "little") -> String; String.unpack(type: String | Class, data: StringLike, at: Long = 0, count: Long? = nil, into: Array? = nil, endian: String = "little") -> Array; String.pack_size(type: String | Class) -> Long **配列メソッド** — a.size() -> Long; a.empty() -> Bool; a.presence() -> Array | Nil; a.push(x: Any) -> Nil (破壊的); a.pop() -> Any (破壊的); a.extend(other: Array) -> Nil (破壊的); a.insert(i: Long, x: Any) -> Nil (破壊的); a.remove_at(i: Long) -> Any (破壊的); a.get(i: Long, fallback: Any) -> Any; a.slice(start: Long, end: Long) -> Array; a.join(sep: String) -> String; a.contains(v: Any) -> Bool; a.index_of(v: Any) -> Long; a.reverse() -> Nil (破壊的); a.map(f: Function) -> Array; a.filter(f: Function) -> Array; a.for_each(f: Function) -> Nil; a.reduce(init: Any, f: Function) -> Any; a.find(f: Function) -> Any; a.any(f: Function) -> Bool; a.all(f: Function) -> Bool; a.flat_map(f: Function) -> Array; a.sum() -> Long | Float; a.product() -> Long | Float; a.min() -> Any; a.max() -> Any; a.min_by(f: Function) -> Any; a.max_by(f: Function) -> Any; a.to_set() -> Set; a.to_object() -> Object; a.group_by(f: Function) -> Object; a.partition(p: Function) -> Tuple; a.unzip() -> Tuple; a.sort(reverse: Bool = false) -> Nil (破壊的); a.sorted(reverse: Bool = false) -> Array; a.sort_by(key: Function, reverse: Bool = false) -> Nil (破壊的); a.sorted_by(key: Function, reverse: Bool = false) -> Array **オブジェクトメソッド** — o.size() -> Long; o.empty() -> Bool; o.presence() -> Object | Nil; o.keys() -> Array; o.values() -> Iterator; o.has(key: String) -> Bool; o.get(key, fallback) -> Any; o.get_or_put(key, init) -> Any (破壊的); o.remove(key: String) -> Nil (破壊的) **セットのメソッド** — size(); empty() -> Bool; presence(); contains(x) -> Bool; union(b); intersect(b); diff(b); sym_diff(b); subset(b) -> Bool; superset(b) -> Bool; to_array(); iter(); add(x); remove(x) **イテレータメソッド** — iter(); it.map(f); it.filter(p); it.take(n); it.skip(n); it.take_while(p); it.skip_while(p); it.step_by(n); it.distinct(); it.tap(f); it.scan(init, f); it.flatten(); it.chunk_by(f); it.chunks(n); it.windows(n); it.flat_map(f); it.chain(other); it.zip(other); it.enumerate(); it.collect(); it.join(sep); it.for_each(f); it.reduce(init, f); it.find(p); it.any(p); it.all(p); it.count(); it.first(); it.last(); it.nth(n); it.position(p); it.contains(v); it.sum(); it.product(); it.min(); it.max(); it.min_by(f); it.max_by(f); it.to_set(); it.to_object(); it.group_by(f); it.partition(p); it.unzip() **コア組み込み関数** — to_long(v: Any, *, base: Long = 10) -> Long; to_float(v: Any) -> Float; to_string(v: Any) -> String; class_of(v: Any) -> Object?; type_of(v: Any) -> String; range(n: Long, *, step: Long = 1) -> Iterator; range(start: Long, end: Long, *, step: Long = 1) -> Iterator; iota(n: Long) -> Array; iota(start: Long, end: Long) -> Array; repeat(n: Long, value: Any) -> Array; grid(x_range: Range, y_range: Range) -> Iterator **Math** — Math.pi; Math.e; Math.inf; Math.nan; Math.abs(x: Long|Float) -> Long|Float; Math.min(a, b, ...) -> Long|Float; Math.max(a, b, ...) -> Long|Float; Math.log(x: Long|Float) -> Float; Math.exp(x: Long|Float) -> Float; Math.sqrt(x: Long|Float) -> Float; Math.sin(x) -> Float; Math.cos(x) -> Float; Math.tan(x) -> Float; Math.asin(x) -> Float; Math.acos(x) -> Float; Math.atan(x) -> Float; Math.atan2(y, x) -> Float; Math.floor(x: Long|Float) -> Long; Math.ceil(...) -> Long; Math.round(...) -> Long; Math.f32(x: Long|Float) -> Float; Math.pow(base: Long, exp: Long) -> Long; Math.sign(x: Long) -> Long; Math.clamp(x: Long|Float, lo: Long|Float, hi: Long|Float) -> Long|Float; Math.wrap(x: Long, n: Long) -> Long **IO** — IO.inspect(x: Any) -> Nil; IO.print(x: Any) -> Nil; IO.println(x: Any = '') -> Nil; IO.input() -> String; IO.stdin() -> reader; .read(); .read(n: Long); .lines(); IO.einspect(x: Any) -> Nil; IO.eprint(x: Any) -> Nil; IO.eprintln(x: Any) -> Nil; IO.stdin_is_terminal() -> Bool; IO.stdout_is_terminal() -> Bool; IO.stderr_is_terminal() -> Bool; IO.capture(f: Function) -> String **FS** — FS.join(parts...: String) -> String; FS.sep() -> String; FS.basename(path: String) -> String; FS.dirname(path: String) -> String; FS.extension(path: String) -> String; FS.stem(path: String) -> String; FS.read(path: String) -> String; FS.write(path: String, content: String) -> Nil; FS.exists(path: String) -> Bool; FS.is_file(path: String) -> Bool; FS.is_dir(path: String) -> Bool; FS.size(path: String) -> Long; FS.list_dir(path: String) -> Array; FS.mkdir(path: String) -> Nil; FS.temp_dir() -> String; FS.mkdtemp(prefix: String) -> String; FS.remove(path: String, recursive: Bool = false) -> Nil; FS.rename(src: String, dst: String) -> Nil; FS.copy(src: String, dst: String, recursive: Bool = false) -> Nil; FS.chmod(path: String, mode: Long) -> Nil; FS.chown(path: String, owner = nil, group = nil) -> Nil; FS.stat(path: String) -> Object; FS.walk(path: String) -> Array; FS.glob(pattern: String) -> Array; FS.watch(path: String, recursive: Bool = true, match: Array? = nil) -> WatchHandle; FS.abspath(path: String) -> String; FS.realpath(path: String) -> String; FS.normpath(path: String) -> String; FS.is_abs(path: String) -> Bool; FS.symlink(target: String, link: String) -> Nil; FS.readlink(path: String) -> String; FS.is_symlink(path: String) -> Bool; p.join(other); p.resolve(); p.exists(); p.is_file(); p.is_dir(); p.read(); p.write(content); p.mkdir(); p.remove(recursive=false); p.rename(dst); p.list(); p.glob(pattern); p.walk(); p.str() **File** — File.open(path: String, mode: String = "r") -> File; File.with(path: String, mode: String = "r", fn: Function) -> Any; f.read() -> String; f.read(n: Long) -> String; f.lines() -> Iterator; f.chunks(n: Long) -> Iterator; f.write(data: String) -> Nil; f.flush() -> Nil; f.seek(offset: Long, whence: String = "set") -> Nil; f.tell() -> Long; f.close() -> Nil **Time** — Time.now() -> Instant; Time.monotonic() -> Float; Time.sleep(secs: Float) -> Nil; Time.from_iso(s: String) -> Instant; Time.from_unix(secs: Long|Float) -> Instant; Time.from_parts(p: Object, utc: Bool = false) -> Instant; Time.parse(s: String, fmt: String) -> Instant; t.iso(utc: true) -> String; t.format(fmt: String, utc: false) -> String; t.parts(utc: false) -> Object; t.weekday(utc: false) -> Long; t.add(years=0, months=0, days=0, hours=0, minutes=0, seconds=0, utc: false) -> Instant; t.start_of(unit: String, utc: false) -> Instant; t.unix() -> Float; t.unix_nanos() -> Long; d.seconds() / .milliseconds() / .minutes() / .hours() / .days() -> Float; d.abs() -> Duration **Random** — Random.seed(n: Long) -> Nil; Random.int(lo: Long, hi: Long) -> Long; Random.uniform(lo: Float, hi: Float) -> Float; Random.gauss(mu: Float, sigma: Float) -> Float; Random.shuffle(a: Array) -> Nil; Random.weighted_choice(pop: Array, weights: Array) -> Any; Random.choice(pop: Array) -> Any **Sys** — Sys.exit(code: Long) -> Nil; Sys.env(name: String, fallback = '') -> Any; Sys.set_env(name: String, value: String) -> Nil; Sys.getcwd() -> String; Sys.chdir(path: String) -> Nil; Sys.data_dir(app: String) -> String; Sys.time() -> Float **Tensor** — Tensor.zeros(...) -> Tensor; Tensor.ones(...); Tensor.randn(...); Tensor.from(arr: Array) -> Tensor; Tensor.concat(parts: Array, axis: Long = 0) -> Tensor; Tensor.where(cond: Tensor, a, b) -> Tensor; Tensor.adam_step(p, m, v, g, lr, beta1, beta2, eps, step) -> Nil; Tensor.index_add(indices: Tensor, values: Tensor, target_shape: Array) -> Tensor; Tensor.scatter_to_axis(indices: Tensor, values: Tensor, size: Long) -> Tensor; Tensor.from_csv(path: String) -> Tensor; Tensor.eval(t1, t2, ...) -> Nil; .shape() -> Array; .dot(other: Tensor) -> Tensor; .linear_sigmoid(x, b) -> Tensor; .pow(exp) -> Tensor; .gt(other) / .lt(other) / .ge(other) / .le(other) / .eq(other) / .ne(other) -> Tensor; .tanh() / .sin() / .cos() -> Tensor; .clamp(lo, hi) -> Tensor; .rope(pos: Long, base) -> Tensor; .causal_attention(k, v) -> Tensor; .layer_norm(gamma, beta) -> Tensor; .transpose() -> Tensor; .permute(axes: Array) -> Tensor; .slice(start, end) -> Tensor; .narrow(params: Array) -> Tensor; .reshape(dims: Array) -> Tensor; .unfold(params: Array) -> Tensor; .pad(params: Array) -> Tensor; .fold(params: Array) -> Tensor; .sum() -> Float; .sum(axis: Long?, keepdims: Bool = false) -> Tensor; .mean() / .mean(axis, keepdims:); .max() / .max(axis, keepdims:); .argmax(axis: Long, keepdims: Bool = false) -> Tensor; .index_select(indices: Tensor) -> Tensor; .softmax_cross_entropy(targets: Tensor) -> Tensor; .to_array() -> Array; .item() -> Float; .requires_grad() -> Tensor; .backward() -> Nil; .grad() -> Tensor; .zero_grad() -> Nil; .detach() -> Tensor; Tensor.use_cpu() -> Nil; Tensor.use_gpu() -> Nil; Tensor.use_auto() -> Nil; Tensor.gpu_available() -> Bool; Tensor.device() -> String; Tensor.profile(fn) -> Array; Tensor.profile_scope(label, fn) -> Any **JSON** — JSON.stringify(v, indent=0, sort_keys=false, lines=false) -> String; JSON.parse(s, lines=false, number_mode='auto', jsonc=false) -> Any **Args** — Args.parse(argv: Array, spec: Object) -> Object; Args.try_parse(argv, spec) -> Object; Args.help(spec: Object) -> String **Proc** — Proc.run(cmd: Array, cwd=nil, env=nil, stdin="", check=false, timeout=0, share=nil, inherit_env=true) -> Object; Proc.all(commands: Array>, limit: Long = , timeout: Long = 0, fail_fast: Bool = false, retries: Long = 0, share: Object? = nil, inherit_env: Bool = true) -> Array; Proc.race(commands: Array>, share: Object? = nil, inherit_env: Bool = true) -> Object; Proc.spawn(cmd: Array, cwd=nil, env=nil, stdin="", share=nil, inherit_env=true) -> handle; h.wait(); h.poll(); h.kill(sig = 15) **Isolate** — Isolate.spawn(fn, *args) -> handle; h.join(); h.poll(); h.drop(); Channel.new(cap = 1) -> (tx, rx); tx.send(v); tx.clone(); rx.clone(); tx.drop(); rx.drop(); rx.recv(); rx.try_recv(); rx.drain(max = nil); Channel.fan_in(items: [rx]) -> rx; Channel.fan_in(items, fn) -> rx; Parallel.map(items, fn, limit = <コア数>); Parallel.each(items, fn, limit = <コア数>); Parallel.map_settled(items, fn, limit = <コア数>); Parallel.race(items, fn, limit = <コア数>); Signal.notify(tx); Signal.reset(); SharedBuffer.new(count, type: Class) -> buffer; SharedBuffer.file(path, count, type: Class) -> buffer; SharedBuffer.shared(count, type: Class) -> buffer; buffer.with_lock(fn) **Matchers** — assert_true(x: Bool, label: String? = nil) -> Nil; assert_false(x: Bool, label: String? = nil) -> Nil; assert_eq(a, b, label: String? = nil) -> Nil; assert_ne(a, b, label: String? = nil) -> Nil; assert_lt(a, b, label: String? = nil) -> Nil; assert_le(a, b, label: String? = nil) -> Nil; assert_gt(a, b, label: String? = nil) -> Nil; assert_ge(a, b, label: String? = nil) -> Nil; assert_throws(kind: String, f: Function) -> Nil; assert_close(a: Float, b: Float, tol: Float, label: String? = nil) -> Nil **Regex** — Regex.compile(pattern) -> Regex; Regex.compile(pattern, flags) -> Regex; Regex.escape(s) -> String; Regex.interp(x) -> String; Regex.find(pattern, s); Regex.match(pattern, s); Regex.find_all(pattern, s); Regex.test(pattern, s); Regex.split(pattern, s); Regex.replace_all(pattern, s, repl) -> String; Regex.replace_first(pattern, s, repl) -> String; re.test(s) -> Bool; re.find(s); re.match(s); re.find_all(s) -> [Match]; re.find_all_str(s) -> [String]; re.find_all_index(s) -> [Int]; re.count(s) -> Int; re.find_iter(s) -> Iterator; re.replace_all(s, repl) -> String; re.replace_first(s, repl) -> String; re.split(s) -> [String] **Http** — json(); Http.get(url, headers=nil, timeout=0, follow_redirects=true); Http.delete(url, headers=nil, timeout=0, follow_redirects=true); Http.head(url, headers=nil, timeout=0, follow_redirects=true); Http.post(url, body="", content_type="text/plain", headers=nil, timeout=0, follow_redirects=true); Http.put(url, body="", content_type="text/plain", headers=nil, timeout=0, follow_redirects=true); Http.request(method, url, body="", content_type="text/plain", headers=nil, timeout=0, follow_redirects=true); Http.sse(url, on_event, headers=nil, timeout=0, follow_redirects=true); Http.client(base_url, headers=nil, timeout=0, follow_redirects=true); Http.server(); Http.ws(url); Http.sse(url, on_event, headers=nil, timeout=0, follow_redirects=true) -> Object; Http.client(base_url, headers=nil, timeout=0, follow_redirects=true) -> Object; Http.server() -> Object; static(mount, dir); sink.write(chunk); bind(port, host="0.0.0.0") -> Long; serve(workers=0); serve_async(workers=0); listen(port, host="0.0.0.0", workers=0); listen_async(port, host="0.0.0.0", workers=0) -> Long; stop(); close(); ws.receive(); ws.try_receive(); ws.send(msg); ws.set_timeout(ms); ws.close(); ws.is_open(); Http.ws(url) -> Object **Encoding** — Encoding.html; Encoding.html.escape(s) -> String; Encoding.html.unescape(s) -> String; Encoding.base64; Encoding.base64.encode(s) -> String; Encoding.base64.decode(s) -> String; Encoding.hex; Encoding.hex.encode(s) -> String; Encoding.hex.decode(s) -> String; Encoding.url; Encoding.url.encode(s) -> String; Encoding.url.decode(s) -> String; Encoding.utf16; Encoding.utf16.encode(s: String, bom: Bool = false) -> String; Encoding.utf16.decode(bytes: String) -> String **Compress** — Compress.gzip(data: String) -> String; Compress.gunzip(data: String) -> String; Compress.deflate(data: String, level: Long = -1) -> String; Compress.zip(files: Object | Dir) -> String **Hash** — Hash.sha256(data: String) -> String; Hash.sha1(data: String) -> String; Hash.sha512(data: String) -> String; Hash.md5(data: String) -> String; Hash.hmac_sha256(key: String, data: String) -> String; Hash.hmac_sha1(key: String, data: String) -> String; Hash.hmac_sha512(key: String, data: String) -> String **CSV** — CSV.parse(text, delimiter=",", header=false, types=nil) -> Array; CSV.stringify(rows: Array, delimiter: String = ",") -> String **Env** — Env.parse(text: String) -> Object; Env.load(path: String = ".env", override: Bool = false) -> Object **UUID** — UUID.v4() -> String; UUID.v7() -> String **Term** — Term.fg(s, n) -> String; Term.bg(s, n) -> String; Term.rgb(s, r, g, b) -> String; Term.red(s); Term.bold(s); Term.dim(s); Term.underline(s); Term.reverse(s); Term.style(fg = nil, bg = nil, bold = false, dim = false, underline = false, reverse = false) -> String; Term.clear() -> String; Term.move(x, y) -> String; Term.hide(); Term.show(); Term.cols(); Term.rows() -> Long; Term.size() -> (Long, Long); Term.width(s) -> Long; Term.flush(); screen.size(); cols(); rows(); screen.clear(); screen.set(x, y, glyph, style = ""); screen.put(x, y, s, style = ""); screen.render() -> String; screen.flush(); screen.poll(timeout) -> Object? **Log** — Log.debug(msg: String, fields: Object = {}); Log.info(msg, fields = {}); Log.warn(msg, fields = {}); Log.error(msg, fields = {}); Log.with(fields: Object) -> logger; Log.set_level(level: String) -> Nil; Log.set_format(format: String) -> Nil **TOML** — TOML.parse(text: String) -> Object; TOML.stringify(v: Object, sort_keys: Bool = false) -> String **XML** — XML.parse(text, keep_space=false) -> Object; XML.events(text, keep_space=false) -> Iterator; el.find(path, namespaces={}) -> Object | Nil; el.find_all(path, namespaces={}) -> Array; el.find_iter(path, namespaces={}) -> Iterator; el.text(path=".", namespaces={}) -> String | Nil; el.stringify(indent=0, declaration=false) -> String; XML.find(el, path, namespaces={}) -> Object | Nil; XML.find_all(el, path, namespaces={}) -> Array; XML.find_iter(el, path, namespaces={}) -> Iterator; XML.text(el, path=".", namespaces={}) -> String | Nil; XML.stringify(el, indent=0, declaration=false) -> String **Vector2** — Vector2.new(x, y); a.hash() -> Long; a.dot(other); a.length(); a.length_squared(); a.normalized(); a.distance_to(other); a.distance_squared_to(other) -> Float; to_string(a) -> String **Vector3** — Vector3.new(x, y, z); a.hash() -> Long; a.dot(other); a.length(); a.length_squared(); a.normalized(); a.distance_to(other); a.distance_squared_to(other) -> Float; to_string(a) -> String **Deque** — Deque.new(); d.push_back(x); d.push_front(x); d.pop_back() -> Any; d.pop_front() -> Any; d.peek_back() -> Any; d.peek_front() -> Any; d.size(); d.empty(); d.to_array() -> Array; d.iter() -> Iterator; to_string(d) **PriorityQueue** — PriorityQueue.new(*, key: Function | Nil = nil, reverse: Bool = false); pq.push(x); pq.pop() -> Any; pq.peek() -> Any; pq.size(); pq.empty(); to_string(pq) -> String **Audio** — Audio.available() -> Bool; sound.play(); sound.stop(); sound.playing() -> Bool; sound.volume(v); sound.pitch(p); sound.pan(p); music.play(); music.stop(); music.pause(); music.resume(); music.seek(seconds); music.playing() -> Bool; music.volume(v); music.pitch(p); music.pan(p); pcm.ready() -> Bool; pcm.needed() -> Long; pcm.push(samples: Array) -> Long; pcm.submit() -> Long; pcm.latency() -> Float; pcm.play(); stop(); pause(); resume(); playing() -> Bool; pcm.volume(v); pcm.pitch(p); pcm.pan(p); Audio.capture_available() -> Bool; mic.ready() -> Bool; mic.start(); running() -> Bool; mic.waiting() -> Long; mic.read(frames = nil) -> Array; Audio.Kauai.new(text: String, voices = nil, drums = nil); Audio.Kauai.load(path: String, voices = nil, drums = nil, dir = nil) **Dir** — Dir.disk(path: String | Path) -> Dir; Dir.memory(files: Object) -> Dir; Dir.embedded(name: String) -> Dir; Dir.zip(path: String | Path = nil, *, bytes: String = nil) -> Dir; d.read(path); d.list_dir(path); d.exists(path); d.is_file(path); d.is_dir(path); d.size(path); d.files(); d.glob(pattern); d.copy_to(dest) ### 一覧ではなく章を読むもの | 名前空間 | 署名 | 何をするもの | |---|---:|---| | `SQLite` | 10 | 組み込みSQLデータベース(query / execute / プリペアド文 / トランザクション) | | `Canvas` | 61 | ゲーム向けイミディエイトモード2Dフレームバッファ(図形 / スプライト / オフスクリーン描画先 / テキスト / キー・マウス・ゲームパッド / ウィンドウ制御) | | `Scene` | 178 | 手続きジオメトリ向けのretained-mode 3Dレンダラ | | `Net` | 22 | 生のTCP / UDPソケットと名前解決(`Http`の下位レイヤ) | | `Desktop` / `Webview` | 10 | ネイティブWebViewのデスクトップアプリ: ローカルHTTPサーバ + ウィンドウを1呼び出しで | | `PEG` | 33 | PEGパーサジェネレータ。文法を書くと構文木が返る | | `CodeGen` | 144 | 小さな言語のIRを手で組み立てて実行する | | `StateMachine` | 8 | 入れ子にできる状態機械。テキストでも書ける | | `FST` | 14 | 書き換えない辞書を圧縮して持つ。前方一致・補完・あいまい検索 | | `Search` | 9 | 自分の文書を全文検索して順位をつける | ## 5. テンプレート ### CLI ツール ```culebra # doctest: skip spec = { name: 'greet', args: [ {name: 'who', doc: 'who to greet'}, {name: 'times', type: 'Long', default: 1, doc: 'repeat count'}, ], } args = Args.parse(Sys.argv, spec) for _ in range(args.times) { println("hello, {args.who}") } ``` ### HTTP リクエスト ```culebra # doctest: skip r = Http.get('https://example.com/api', headers: {Accept: 'application/json'}) if !r.ok { throw "HTTP {r.status}: {r.reason}" } println(r.json().title) ``` ### ファイル処理 ```culebra # doctest: skip counts = File.with('input.txt', 'r', fn (f) { mut n = {} for line in f.lines() { for w in line.trim().lower().split(' ') { k = w.to_string() n[k] = n.get(k, 0) + 1 } } n }) for k in counts.keys().sorted() { println("{k}\t{counts[k]}") } ``` ### テストファイル ```culebra # doctest: skip # test_math.cul @test fn adds() { assert_eq(1 + 2, 3) } @parametrize([(1, 2, 3), (10, 20, 30)]) fn adds_each(a, b, want) { assert_eq(a + b, want) } ``` 次に読むもの ------------ - 理由付きのチュートリアル: [`handbook.ja.md`](handbook.ja.md) - 形式文法と評価規則: [`language.ja.md`](language.ja.md) - 散文付きの完全なAPIリファレンス: [`stdlib.ja.md`](stdlib.ja.md) - バイナリビルドと埋め込み: [`deployment.ja.md`](deployment.ja.md)