このページはまだ翻訳されていません。原文の内容が表示されています。
state
文書中の状態の管理。
文書中で何回か計算を行い、最後の計算結果を次の計算で使用するために記憶しておきたいとします。 以下と同等のコードを試すと10、13、26、21と出力されることを期待するでしょう。 しかしTypstではそうはなりません。 このコードを試してみると、Typstは Variables from outside the function are read-only and cannot be modified. というエラーメッセージを出力することが分かります。
// This doesn't work!
#let x = 0
#let compute(expr) = {
x = eval(
expr.replace("x", str(x))
)
[New value is #x. ]
}
#compute("10") \
#compute("x + 3") \
#compute("x * 2") \
#compute("x - 5")
状態と文書のマークアップ
なぜこうなるのでしょうか? 一般的に副作用を伴うこの手の計算は文書のマークアップにおいて問題を引き起こすためで、Typstではこれをエラーとして扱います。 この結果を理解するには、計算処理が文書内で生成物がレイアウトされる順序と同じ順序で行われる必要があります。 今回の単純な例ではこの条件が満たされますが、一般的には必ずしもそうとは限りません。
見出しの番号付けという、類似した状態ですが、少し異なる例を見てみましょう。 各見出しで見出しカウンターの値を増やしたいとします。 簡単そうですよね? ただ1を足すだけです。 残念ながらそう単純ではないのです。 以下の例を考えます。
#set heading(numbering: "1.")
#let template(body) = [
= Outline
...
#body
]
#show: template
= Introduction
...

ここで、Typstはまずshowルール以降の文書本体を処理し、Introduction見出しを検知します。
続いてtemplate関数に生成コンテンツを渡します。
その後、初めてOutlineを検知します。
単にカウンター値を増やすとIntroductionは1に、Outlineは2となります。
Typstにおける状態管理
それでは代わりにどうするのでしょうか?
Typstの状態管理システムを使用します。
識別用のキーとなる文字列とオプションの初期値とともにstate関数を呼び出すことで状態値が得られます。
この状態値はいくつかの関数を公開しており、最も重要な2つの関数がgetとupdateです。
-
get関数は状態の現在値を取得します。 値は文書中で変化するため、これはコンテキストが利用可能な場合にのみ使用できる コンテキスト 関数です。 -
update関数は状態に修正を加えます。 任意の値が使用できます。 関数ではない値が渡された場合、状態にその値が設定されます。 関数が与えられた場合、その関数は前の状態を受け取り、新しい状態を返さなければなりません。
最初の例は以下のようになります。
#let s = state("x", 0)
#let compute(expr) = [
#s.update(x =>
eval(expr.replace("x", str(x)))
)
New value is #context s.get().
]
#compute("10") \
#compute("x + 3") \
#compute("x * 2") \
#compute("x - 5")

Typstが管理する状態は常に評価順ではなくレイアウト順で更新されます。
updateメソッドはコンテンツを返し、その影響は文書に返されたコンテンツが挿入された場所で生じます。
このようにして、計算結果を変数に保存できるようになり、正しい結果を表示しています。
...
#let more = [
#compute("x * 2") \
#compute("x - 5")
]
#compute("10") \
#compute("x + 3") \
#more

この例はもちろん少々極端ですが、これが実際に本当に必要となることがよくあります! 良い例は見出しカウンターです。 これはTypstのカウンターシステムが状態システムにとてもよく似ているためです。
タイムトラベル
Typstの状態管理システムを使用するとタイムトラベルもできます!
文書内の任意の位置でその状態がどの値になっているのかを、どこからでも突き止めることができます。
特に、atメソッドを用いると特定の任意の位置での状態値が取得でき、finalメソッドを用いると文書の終わりでの状態値を取得できます。
...
Value at `<here>` is
#context s.at(<here>)
#compute("10") \
#compute("x + 3") \
*Here.* <here> \
#compute("x * 2") \
#compute("x - 5")

注意事項
全ての状態値を解決するために、Typstはコードを複数回評価します。 しかしながら、実際に状態操作が完全に解決されるかは保証されません。
例えば、状態の最終的な値に依存する更新を行う状態を作成した場合、決して収束しなくなるでしょう。
以下の例はこの実演です。
状態を1で初期化し、続いて自身の最終値に1を足した値に更新します。
したがって値は2になるべきですが、最終値が2となったので3に更新します。以下同様です。
この例では有限値が表示されていますが、これは単にTypstが数回試行した後に諦めるためです。
// This is bad!
#let s = state("x", 1)
#context s.update(s.final() + 1)
#context s.get()

一般に、コンテキスト内部で更新を行う状態を作成しないようにしてください。 可能であれば、更新内容をコンテキストに依存しない値として、あるいは前の値から新しい値を計算する関数として定義してください。 どうしても避けられない場合がありますが、その場合は結果が適切に収束することを保証することはあなたの責任です。
コンストラクタ引数引数は関数への入力値です。関数名の後に括弧で囲んで指定します。
キーで識別される新しい状態の作成。
key
状態を識別するキー。
initany位置引数位置引数位置引数は順序通りに指定することで、引数名を省略して設定できます。
状態の初期値。
デフォルト値: none
定義定義これらの関数や型には、関連する定義を持たせることができます。定義にアクセスするには、対象の関数や型の名前を指定した後に、ピリオド区切りで定義名を記述します。
getコンテキスト関数コンテキスト関数コンテキスト関数は、コンテキストが既知の場合にのみ使用できます。
現在のロケーションでの状態値を取得。
これはstate.at(here())と等価です。
atコンテキスト関数コンテキスト関数コンテキスト関数は、コンテキストが既知の場合にのみ使用できます。
finalコンテキスト関数コンテキスト関数コンテキスト関数は、コンテキストが既知の場合にのみ使用できます。
文書の終わりでの状態値の取得。
update
状態値を更新。
更新は、返り値であるコンテンツが文書中に挿入された位置で適用されます。
文書中に出力がなければ何も起こりません!
例えばlet _ = state("key").update(7)と書いた場合が、この何も起きないときに該当します。
状態の更新は常にレイアウト順に適用されるため、この場合にはTypstはいつ状態を更新するのか分かりません。
update
関数ではない値が与えられた場合、状態にその値を設定します。 関数が与えられた場合、その関数は前の状態を受け取り、新しい状態を返さなければなりません。