関数したい(#let

 1// レポートの表紙を出力する関数を定義
 2#let cover(
 3    title,
 4    author,
 5    date: datetime.today().display()
 6) = {
 7  block()[
 8    #title
 9  ]
10
11  block()[
12    #author
13  ]
14
15  block()[
16    #date
17  ]
18}
19
20// 表紙を出力
21#cover("レポートのタイトル", "著者名", date: "2026/01/01")

#letキーワードで関数を定義できます。 ここではレポートの表紙を出力する関数を作ってみました。 定義した関数は#関数名で、本文の任意の箇所で呼び出すことができます。

位置引数したい

 1#let cover(title, author, date) = {
 2  block()[
 3    #title
 4  ]
 5  block()[
 6    #author
 7  ]
 8  block()[
 9    #date
10  ]
11}
12
13#cover("レポートのタイトル", "著者名", "日付")

位置引数(positional arguments)は、 位置で識別される引数です。 関数が呼び出された時に、最初から順番に処理されます。

名前付き引数したい

 1#let cover(title: none, author: none, date: none) = {
 2  block()[
 3    #title
 4  ]
 5  block()[
 6    #author
 7  ]
 8  block()[
 9    #date
10  ]
11}
12
13#cover(title: "レポートのタイトル", author: "著者", date: "日付")
14#cover("レポートのタイトル", "著者名", "日付")  // => エラー

名前付き引数(named arguments)は 変数名: の形式で指定する引数です。 関数を定義するときにデフォルト値を指定すると、名前付き引数にできます。

注釈

1#let cover(
2    title,
3    author,
4    date: datetime.today().display()
5) = {...}
6
7#cover("レポートのタイトル", "著者名")  // => 日付は自動取得
8#cover("レポートのタイトル", "著者名", date: "2026/01/16")  // => 日付を固定

位置引数と名前付き引数は混在できます。 Pythonなど多くのプログラミング言語と同じで、 位置引数を先に定義する必要があります。

可変長引数したい

 1#let cover(title, ..authors, date: datetime.today().display()) = {
 2  block()[
 3    #title
 4  ]
 5  block()[
 6    #authors.pos().join(", ")
 7  ]
 8  block()[
 9    #date
10  ]
11}
12
13#cover(
14  "タイトル",
15  "著者1", "著者2", "著者3",
16  date: "2026/01/16",
17)

スプレッド演算子(..)で可変長引数を定義できます。 位置引数を配列にする場合は.pos()、 名前付き引数を辞書型にする場合は.named()で変換できます。

注釈

複数の著者を指定する場合はよくあるため、可変長引数のサンプルとしてみましたが、わざわざ可変長引数にするより、そのまま配列として渡せるようにしたほうが可読性がよさそうです。

 1#cover(
 2    title,
 3    authors: (),
 4    date: datetime.today().display()) = {
 5  block()[
 6    #title
 7    ]
 8  block()[
 9    // authorsは配列なので pos() は不要
10    #authors.join(",")
11  ]
12  block()[
13    #date
14  ]
15}
16
17#cover(
18  "タイトル",
19  authors: ("著者1", "著者2", "著者3"),
20  date: "2026/01/16")

コンテンツブロックしたい

1#cover(
2  [*レポートのタイトル*],
3  authors: ("著者1", "著者2", "著者3"),
4  date: "2026/01/16",
5)

角括弧([...])で囲んだ内容は「コンテンツブロック」と認識され、マークアップしたコンテンツをそのまま渡すことができます。 関数の引数にコンテンツブロックをそのまま渡すことができます。

注釈

コンテンツブロックを渡すサンプルとして、太字にしたレポートタイトルを引数として渡しましたが、このようなタイトル装飾は関数で定義するほうが実用的です。

条件分岐したい(if-else

 1#let cover(
 2  title: none,
 3  authors: (),
 4  date: datetime.today().display(),
 5) = {
 6  if title != none {
 7    block()[
 8      #title
 9    ]
10  }
11
12  if authors.len() > 0 {
13    block() [
14      #authors.jon(", ")
15    ]
16  }
17
18  if date == none {
19    // 日付を非表示
20  } else {
21    block()[
22      #date
23    ]
24  }
25}
26
27#cover(
28  title: [*レポートのタイトル*],
29  authors: ("著者1", "著者2"),
30  date: none,  // 非表示
31)

if-else構文で条件分岐できます。 #cover関数のすべての引数を名前付きにして、 デフォルト動作で表示/非表示を分けてみました。

ループ処理したい(for

 1#cover(
 2  title: none,
 3  authors: (),
 4  date: datetime.today().display()
 5) = {
 6  // title表示は省略
 7  if authors.len() > 0 {
 8    for author in authors {
 9      block()[
10        #author
11      ]
12    }
13  }
14}

forで配列に対してループ処理できます。 このサンプルでは、著者を別行(別ブロック)で表示するようにしました。

ループ制御したい(break / continue

1#let count-to-limit(limit) = {
2  for i in range(100) {
3    if i == limit { break }
4    if calc.rem(i, 2) == 0 { continue }
5    [#i ]
6  }
7}
8
9#count-to-limit(10)

break / continueでループ処理を制御できます。

実践的な関数パターン

パターン1: スタイル設定の再利用

 1#let heading-style = (
 2  size: 16pt,
 3  weight: "bold",
 4  fill: navy
 5)
 6
 7#let custom-heading(body) = {
 8  text(..heading-style, body)
 9}
10
11#custom-heading[Chapter 1]

パターン2: 複数の処理を組み合わせる

 1#let code-box(code, lang: "typst") = {
 2  block(
 3    fill: luma(240),
 4    inset: 10pt,
 5    radius: 4pt,
 6    [
 7      *#lang*
 8
 9      #raw(code)
10    ]
11  )
12}
13
14#code-box("let x = 5", lang: "rust")

パターン3: 条件に応じた異なる出力

 1#let note(title, severity: "info") = {
 2  let color = if severity == "warning" {
 3    orange
 4  } else if severity == "error" {
 5    red
 6  } else {
 7    blue
 8  }
 9
10  rect(
11    fill: color.lighten(80%),
12    stroke: 1pt + color,
13    inset: 8pt,
14    [*#upper(severity):* #title]
15  )
16}
17
18#note("All is well", severity: "info")
19#note("Watch out!", severity: "warning")

パターン4: データの変換と集約

 1#let filter-and-format(..data) = {
 2  data
 3    .pos()
 4    .filter(x => type(x) == int and x > 0)
 5    .map(x => [Item: #x])
 6    .join(", ")
 7}
 8
 9#filter-and-format(1, -2, 3, "text", 5)
10// 結果: Item: 1, Item: 3, Item: 5

show-setルールで関数を使う

.where セレクターと組み合わせて、特定の要素に関数を適用:

1// 見出しにカスタム装飾を適用
2#let decorated-heading(it) = {
3  [ #it.body ]
4}
5
6#show heading.where(level: 2): it => decorated-heading(it)
7
8== Section Title

無名関数したい

1#let custom-heading = it => [
2  #upper(it.body)
3]
4
5#show heading: custom-heading

=>を使って無名関数(unnamed functions)を定義できます。 Show rulesと合わせて使うことが多いです。

注釈

上記サンプルではcustom-headingという変数に無名関数を代入していますが、通常は #showルールに直接指定します。

1#show heading: it => [
2  #upper(it.body)
3]

注釈

無名関数を使うときには、慣習でitという変数名を使うようです。 itが持つデータはfields()メソッドで確認できます。

1#show heading: it => [
2  #it.fields()
3]

関数内での変数スコープ

変数は定義されたブロック内でのみアクセス可能です:

 1#let process() = {
 2  let temp = "temporary value"
 3  [#temp]
 4}
 5
 6#process()
 7// #temp  ← エラー: scope外
 8
 9#temp = "これは外側の変数"
10#temp   OK

ベストプラクティス

  1. デフォルト引数を活用する - 柔軟で使いやすい関数を作成

  2. 名前付き引数を使う - 引数が3つ以上の場合は名前付きにする

  3. コンテンツ型を活用する - マークアップを関数で装飾・加工

  4. 引数の展開を使う - 設定を再利用可能な辞書として管理

  5. 単一責任 - 1つの関数は1つのことに集中させる

  6. わかりやすい名前 - 関数の役割が名前から明確にわかるように

関連ドキュメント

関数の応用例については、以下も参照してください: