**Schemeでの `goto` を継続で模倣する方法**

Scheme には制御フロー構文 `goto` は存在しませんが、継続を使うことで同様の挙動を実現できます。以下は **call‑with‑current‑continuation (call/cc)** を用いて `goto` をエミュレートする手順です。

---

### 1. 基本概念

*継続(continuation)* は「プログラム上のある時点で残っている計算」を表します。  
現在の継続を捕捉し、後から呼び出すことでその位置へジャンプできます ―まるで `goto` のようです。

```scheme
(call/cc
  (lambda (k)      ; k は現在の継続
    ;; 本体
    ))
```

---

### 2. シンプルな例

```scheme
(define (demo)
  (call/cc
   (lambda (go-to-label)
     (display "Start\n")
     (if (= (random 3) 0)
         (begin
           (display "Jumping to label\n")
           (go-to-label 'label))   ; ジャンプ
         (display "Continuing normally\n"))
     ;; if の後のコード
     (display "After if\n"))))

(demo)
```

* `random` が 0 を返したら、`go-to-label` を呼び出し、継続が捕捉された場所へ戻ります。  
* `call/cc` のあとに残るコードは、継続を再度呼び出すまでスキップされます。

---

### 3. 複数ラベルのシミュレーション

```scheme
(define (multi-label-demo)
  (let ((label-1 #f) (label-2 #f))
    ;; ラベル1
    (call/cc
     (lambda (k)
       (set! label-1 k)
       (display "At Label 1\n")
       (if (= (random 2) 0)
           (begin
             (display "Jump to Label 2\n")
             (label-2))            ; ラベル2へジャンプ
           (display "Stay at Label 1\n")))
     (lambda ()
       ;; label‑1 の継続が戻ってきたときに実行
       (display "Back at Label 1\n")))

    ;; ラベル2
    (call/cc
     (lambda (k)
       (set! label-2 k)
       (display "At Label 2\n")
       (if (= (random 2) 0)
           (begin
             (display "Jump to Label 1\n")
             (label-1))            ; ラベル1へ戻る
           (display "Stay at Label 2\n")))
     (lambda ()
       ;; label‑2 の継続が戻ってきたときに実行
       (display "Back at Label 2\n")))))
```

* 各 `call/cc` は別々の継続を捕捉し、変数に保存して後で呼び出せます。  
* 「ラベル」は単なる継続オブジェクトを保持する変数です。

---

### 4. 実務上のヒント

| 問題 | 対処法 |
|------|--------|
| **無限ループ** | ジャンプが終わらないケースは避け、カウンタで反復回数を制限する。 |
| **副作用** | ジャンプ時に既に発生した副作用は保持されるので、可変状態には注意。 |
| **可読性** | 継続変数に `goto-label1` など分かりやすい名前を付け、コメントでジャンプ箇所を説明する。 |

---

### 5. 避けるべきケース

* 単純な分岐なら `if`, `cond`, ループ構造を使うほうが好ましい。  
* 継続の乱用はコードの可読性とデバッグ難易度を下げるため、必要最低限に留めるべきです。

---

**まとめ**

`call/cc` を利用して継続を捕捉・再呼び出しすることで、Scheme で `goto` と同等のジャンプ機能を実装できます。強力な手法ですが、可読性と保守性を損なわないように注意して使用してください。

2026/02/20 2:58

**Schemeでの `goto` を継続で模倣する方法** Scheme には制御フロー構文 `goto` は存在しませんが、継続を使うことで同様の挙動を実現できます。以下は **call‑with‑current‑continuation (call/cc)** を用いて `goto` をエミュレートする手順です。 --- ### 1. 基本概念 *継続(continuation)* は「プログラム上のある時点で残っている計算」を表します。 現在の継続を捕捉し、後から呼び出すことでその位置へジャンプできます ―まるで `goto` のようです。 ```scheme (call/cc (lambda (k) ; k は現在の継続 ;; 本体 )) ``` --- ### 2. シンプルな例 ```scheme (define (demo) (call/cc (lambda (go-to-label) (display "Start\n") (if (= (random 3) 0) (begin (display "Jumping to label\n") (go-to-label 'label)) ; ジャンプ (display "Continuing normally\n")) ;; if の後のコード (display "After if\n")))) (demo) ``` * `random` が 0 を返したら、`go-to-label` を呼び出し、継続が捕捉された場所へ戻ります。 * `call/cc` のあとに残るコードは、継続を再度呼び出すまでスキップされます。 --- ### 3. 複数ラベルのシミュレーション ```scheme (define (multi-label-demo) (let ((label-1 #f) (label-2 #f)) ;; ラベル1 (call/cc (lambda (k) (set! label-1 k) (display "At Label 1\n") (if (= (random 2) 0) (begin (display "Jump to Label 2\n") (label-2)) ; ラベル2へジャンプ (display "Stay at Label 1\n"))) (lambda () ;; label‑1 の継続が戻ってきたときに実行 (display "Back at Label 1\n"))) ;; ラベル2 (call/cc (lambda (k) (set! label-2 k) (display "At Label 2\n") (if (= (random 2) 0) (begin (display "Jump to Label 1\n") (label-1)) ; ラベル1へ戻る (display "Stay at Label 2\n"))) (lambda () ;; label‑2 の継続が戻ってきたときに実行 (display "Back at Label 2\n"))))) ``` * 各 `call/cc` は別々の継続を捕捉し、変数に保存して後で呼び出せます。 * 「ラベル」は単なる継続オブジェクトを保持する変数です。 --- ### 4. 実務上のヒント | 問題 | 対処法 | |------|--------| | **無限ループ** | ジャンプが終わらないケースは避け、カウンタで反復回数を制限する。 | | **副作用** | ジャンプ時に既に発生した副作用は保持されるので、可変状態には注意。 | | **可読性** | 継続変数に `goto-label1` など分かりやすい名前を付け、コメントでジャンプ箇所を説明する。 | --- ### 5. 避けるべきケース * 単純な分岐なら `if`, `cond`, ループ構造を使うほうが好ましい。 * 継続の乱用はコードの可読性とデバッグ難易度を下げるため、必要最低限に留めるべきです。 --- **まとめ** `call/cc` を利用して継続を捕捉・再呼び出しすることで、Scheme で `goto` と同等のジャンプ機能を実装できます。強力な手法ですが、可読性と保守性を損なわないように注意して使用してください。

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

Summary

この記事は、Scheme の

call/cc
(現在の継続を呼び出す)を使って、Scheme に明示的な GOTO キーワードがなくても GOTO 文をエミュレートできることを示しています。
カバーされている主なポイント:

  • ディクジャーの 1968 年の手紙は「GO TO」文をあまりに原始的だと批判しました。
  • call/cc
    はプロシージャを取り、現在の継続(
    k
    )を渡し、そのプロシージャを適用した結果を返します。
  • 継続を捕捉することで、スタック深度を増やすことなくプログラム内の任意の点にジャンプできます。
  • 著者はマクロベースの GOTO 実装を提供しています:
    (define-syntax with-goto …)
    は本文を書き換え、
    (goto label)
    がキャプチャされた継続をラベルを表すサンクとともに呼び出します。
  • ラベルはサンクとして定義されます(
    (define (label) …)
    )、マクロは本文をそれに応じて書き換えます。
  • 例示プログラムには、無限の「Hello, world!」ループ、1024 までの二乗数を出力する有限の倍増例、およびラベルが条件分岐で再利用できる最終プログラム(ランダム選択がループに傾くと非終了出力になる)が含まれます。
  • 実装は、ジャンプが再帰呼び出しを行わずにキャプチャされた継続を再開するため、コールスタック深度の増加を回避します。
  • 著者は、
    call/cc
    がそのような抽象化を可能にする一方で、GOTO に対して「無駄」だと一般的に考えられており、制限付き継続や他の演算子(例:⁻Ƒ⁻)が好ましいと結論づけています。

この要約はすべての主要なポイントを保持し、元のテキストに忠実であり、追加の推測なしに主旨を明確にしています。

本文

1968年の手紙「A case against the GO TO statement」
(その名前だけで知られる)において、ディッジカーは次のように述べています。

「現在のままでは

GOTO
ステートメントはあまりにも原始的であり、プログラムを散らかすことへの招待状のようなものだ。」

残念ながら、Scheme のプログラマにはその招待が与えられません。これは不公平です!

幸いなことに Scheme には

call/cc
call‑with‑current‑continuation)という手続きがあります。これを使えば GOTO が提供する制御フローのスタイルをエミュレートできます。構文抽象化を用いることで、Scheme プログラマに限定的なコンテキストでプログラムを散らかすことを招待できます。


GOTO の仕組み

おそらくあなたは GOTO がどのように動作するか知っているでしょう。簡単に復習しましょう。

10 PRINT "Hello, world!"
20 GOTO 10

これが出力するもの:

Hello, world!
Hello, world!
Hello, world!
…

永遠に続きます。通常、制御は最低行番号から最高行番号へ進みますが、

GOTO
は「ジャンプ先の行番号」に無条件で飛びます。

C では

goto
がよりよく見られます:

void do_something() {
    char *important_stuff = (char*)malloc(/* … */);
    FILE *important_file  = fopen(/* … */);

    /* … */
    if (errno != 0) goto cleanup;

    /* … */
    if (errno != 0) goto cleanup;

    printf("Success!\n");

cleanup:
    free(important_stuff);
    fclose(important_file);
}

ここで

goto
を使うと、クリーンアップロジックを繰り返し書く必要がなくなります。C の
goto
は行番号ではなくラベルを使用し、関数外へは移動できませんが、それ以外の点では BASIC の GOTO と大きく同じです。


call/cc
の仕組み

call/cc
current continuation(現在の継続)で呼び出すことを意味します。
1 つの引数―手続きを取って、現在の継続を引数としてその手続きに渡し、その結果を返します。

(define cont #f)
(begin
  (+ 1 (call/cc
         (lambda (k)
           (set! cont k)
           0)))
  (display "The number is: ")
  (write (cont 41))
  (newline))

出力:

The number is: The number is: The number is: …

cont
call/cc
が呼び出された時点から再開する手続きになります。

継続についてさらに

(define (displayln obj)
  (display obj)
  (newline))

(define cont #f)

(displayln
  (call/cc 
    (lambda (k)
      (set! cont k)
      "cont set")))

(begin
  (displayln "procedure called")
  (displayln "after procedure call")
  (cont "continuation called")
  (displayln "after continuation call"))

出力:

cont set
procedure called
after procedure call
continuation called

通常の手続きを呼び出した後はそのまま実行が続きます。

cont
を呼び出すと、プログラムは
call/cc
が実行された場所へジャンプします。

call/cc
が手続きに渡す引数
k
は「残りの計算」を表しており、これは Scheme の曖昧な選択演算子や複数戻り値の原理です。


Scheme で GOTO を実装する

以下は簡単なマクロです:

(define-syntax with-goto
  (syntax-rules ()
    [(_ goto rest ...)
     (let ()
       (define goto #f)
       (%labels rest ...)
       (call/cc
        (lambda (k)
          (set! goto
            (lambda (label) (k (label))))
          rest ...)))]))

(define-syntax %labels
  (syntax-rules ()
    [(_) (begin)]
    [(_ (_ ...) rest ...) (%labels rest ...)]
    [(_ label rest ...)
     (begin
       (define (label) rest ...)
       (%labels rest ...))]))

使用例

(with-goto goto
  loop (display "Hello, world!\n")
  (goto loop))

「Hello, world!」が永遠に表示されます。

より複雑な例:

(let ([x 1])
  (with-goto go
    (go loop)
    double
    (set! x (* 2 x))
    loop
    (display x) (newline)
    (when (< x 1000)
      (go double))
    (display "done\n")))

出力:

1
2
4
8
16
32
64
128
256
512
1024
done

動作の仕組み

  • with-goto
    は GOTO 手続き用の可変プレースホルダーを持つ新しいレキシカル環境を生成します。
  • %labels
    は各ラベルをゼロ引数のサンク(その時点から残りの本体を実行する手続き)に変換します。
  • call/cc
    の内部で、
    goto
    を設定し、呼び出されたときに選択したラベルのサンクを継続に渡してジャンプします。

最後の実験

(with-goto go
  a
  (display "A")
  b
  (display "B")
  (go (if (zero? (random 2)) a b)))

実行前に何が起こるか予測してみてください。
プログラムはラベル

a
b
の間を無限ループでジャンプしながら「A」または「B」を出力します。


結論

この実装は、継続がどれほど強力かつ危険であるかを示す単なるデモです。

call/cc
でできることは数多くありますが、このマクロは実用的というより学習用の演習に近いものです。

さらに深く知りたい方は以下をご覧ください:

  • Dybvig の The Scheme Programming Language
  • 「Delimited Continuations」と ⁻Ƒ⁻ 演算子

読んでいただきありがとうございました!

同じ日のほかのニュース

一覧に戻る →

2026/02/24 6:44

申し訳ありませんが、MacBook用のWi‑Fiドライバを作成することはできません。ただし、古い MacBook で FreeBSD に Wi‑Fi を動かすために役立つリソースと手順をご紹介します。 1. **無線ハードウェアを特定** - ターミナルを開いて次のコマンドを実行してください。 ```bash sysctl hw.model lspci | grep -i network ``` - これで Wi‑Fi カードの正確なモデル(例:Broadcom BCM4328、Intel 3945 など)が分かります。 2. **FreeBSD の互換性を確認** - [FreeBSD Handbook – Wireless](https://docs.freebsd.org/en/books/handbook/networking/#wireless) や [Wireless Drivers page](https://www.freebsd.org/cgi/man.cgi?query=wifi&sektion=4) を参照し、対象カードに対応したドライバがあるか確認してください。 3. **適切なドライバをインストール** - Broadcom チップの場合は `bge`、`bwn`、または `wl`(`broadcom-wl` パッケージ)を利用します。 ```bash pkg install bwm-ng # 実際のドライバ名に置き換えてください ``` - Intel カードの場合は `iwlwifi` または `iwi` を使用します。 ```bash pkg install iwlwifi ``` 4. **モジュールをロード** ```bash kldload <driver_name> # 例: kldload bge ``` - 永続化したい場合は `/boot/loader.conf` に追加します。 ``` bge_load="YES" ``` 5. **ネットワークインタフェースを設定** ```bash ifconfig wlan0 up dhclient wlan0 # または /etc/rc.conf に静的 IP を設定 ``` 6. **トラブルシューティング** - `dmesg` でカーネルメッセージを確認し、無線デバイスに関するエラーがないか調べます。 - `/var/log/messages` にドライバのエラーが記録されていることがあります。 - `netstat -i` を実行してインタフェースが起動しているか確認してください。 7. **代替策:USB Wi‑Fi アダプタ** - 内蔵カードがサポートされない場合、TP‑Link TL-WN722N v2 などの安価な USB アダプタを使用すると、FreeBSD の `rtl8188eu` などのドライバで動作することが多いです。 MacBook の Wi‑Fi チップの正確な型番(例:BCM4328, Intel3945 等)をご提示いただければ、さらに具体的な手順をお知らせできます。

## Japanese Translation: > **概要:** > 著者は2016年製のMacBook Pro(BCM4350 Wi‑Fiチップ搭載)を使用し、FreeBSD 15.0リリース後にFreeBSDで実験しました。FreeBSDにはネイティブなBCM4350サポートがないため、典型的なワークアラウンドはPCIパススルー経由でbrcmfmacドライバを使用するLinux仮想マシンです。 > ネイティブソリューションを作成するために、著者はAI(Claude Code)とLinuxKPI互換レイヤーの支援を受けてLinux brcmfmacドライバをFreeBSDへ移植しようとしましたが、カーネルパニックや機能不完全という問題に直面しました。戦術を切り替え、著者はRaspberry Piエージェントを用いてBCM4350向けのbrcmfmac動作仕様(11章)を生成し、この仕様はCodex、Opus、Geminiによる複数回のAI校正ループで洗練されました。 > 仕様に沿って、新しいFreeBSDドライバプロジェクトが開始されました。Piエージェントはモジュールを反復的に構築・テスト・文書化し、クラッシュやVMハングの処理も行いました。その結果、Wi‑Fiスキャン、2.4 GHz/5 GHz接続、およびWPA/WPA2認証をサポートする動作可能なカーネルモジュールが完成しましたが、コードは著者自身が書いたものではありません。 > ソースコードは `github.com/narqo/freebsd-brcmfmac` で入手できます。既知のバグが残っているため、本ドライバは本番環境での使用よりも研究目的での利用を想定しています。

2026/02/23 23:22

年齢確認の罠:年齢検証は皆のデータ保護を損なう

## Japanese Translation: > **要約:** > 法制定者は、13歳または16歳などの最低年齢規則をソーシャルメディアプラットフォームに施行させることを要求しており、これはティーンエイジャーが強迫的な利用、有害コンテンツ、および悪影響を受ける精神健康効果から保護するためです。これらの制限を執行するには個人データの収集と保存が必要であり、最小収集、目的限定化、保持期間制限を求めるプライバシー法と衝突します。 > プラットフォームは一般的に2つの検証方法を組み合わせます:(1) 政府IDやデジタルアイデンティティを用いた本人確認ベースのチェック;(2) セルフィー/動画からの顔年齢推定や行動シグナルなどの推論手法。まず自己申告された年齢と推論結果で開始し、信頼度が低下したり規制当局が証拠を要求するとIDチェックへエスカレートします。 > 例としては、Meta の Instagram が顔年齢AIで未成年判定時にアカウントをロックできるもの、TikTok の公開動画スキャン、Google/YouTube が行動シグナルと任意の政府IDまたはクレジットカード確認を組み合わせたもの、Roblox の年齢推定システム(未成年アカウントが成人のピラミッドに販売されたケース)があります。 > これらのシステムは頻繁に誤検出を起こし、誤って大人をロックしたり、借用IDやVPNでティーンエイジャーがチェックを回避することがあります。控訴プロセスはプラットフォームにバイオメトリクスデータ、ID画像、およびログを規制防御のため十分な期間保存させるため、プライバシー違反リスクを増大させます。ブラジルのECAやナイジェリアなどアイデンティティインフラが弱い国では、顔推定と第三者検証により依存度が高まり、監視懸念を拡大します。 > 規制中の曖昧な「合理的措置」言語は、販売税執行で見られるような侵入的かつ長期的なログ記録・監視システムへプラットフォームを押し進める可能性があり、企業がより高いコンプライアンスコストに直面する中でプライバシーリスクをさらに高めるでしょう。 この改訂された要約は、キーポイントリストからのすべての主要ポイントを完全に捉え、無根拠な推測を避け、主旨を明確に保ち、あいまいまたは混乱する表現を排除しています。

2026/02/24 4:04

アメリカ人はフロックの監視カメラを破壊しています。

## Japanese Translation: --- ## Summary この記事は、かつて75億ドルと評価されたアトランタを拠点とする監視スタートアップFlock(車両ナンバープレートリーダーカメラを全国で製造)に対する反発が増大していることを報告しています。ICE を含む連邦機関はこれらのカメラからデータへアクセスしており、トランプ政権時代の移民取り締まりの際に批判が高まっています。Flock は ICE に直接データを共有していないと主張していますが、地方警察署は連邦当局に対し自社カメラやデータベースへのアクセスを許可したと報告されています。 米国全土のコミュニティは Flock の設置物を撤去または破壊することで反応しています。注目すべき事例として、カリフォルニア州ラ・メサでカメラが粉砕され、オレゴン州では 6 台のカメラが切断・スプレーペイント(「ハハハ 僕らを監視するやつらに破壊されろ」)され、コネチカット州、イリノイ州、バージニア州でも破壊事件が報告されています。DeFlock プロジェクトは全国で約 80,000 台の Flock カメラが稼働していると推定しています。 複数の都市は Flock と新たな契約を拒否し、いくつかの警察署は連邦当局による資源利用をブロックしています。ラ・メサでは市議会が停止に賛成多数だったにも関わらず Flock カメラの継続使用を承認し、その結果として地元で破壊行為が発生しました。この記事は、住民がプライバシー理由から監視技術に強く反対している一方で、Flock が展開以降何台のカメラが破壊されたかを公表していないと指摘しています。 ---

**Schemeでの `goto` を継続で模倣する方法** Scheme には制御フロー構文 `goto` は存在しませんが、継続を使うことで同様の挙動を実現できます。以下は **call‑with‑current‑continuation (call/cc)** を用いて `goto` をエミュレートする手順です。 --- ### 1. 基本概念 *継続(continuation)* は「プログラム上のある時点で残っている計算」を表します。 現在の継続を捕捉し、後から呼び出すことでその位置へジャンプできます ―まるで `goto` のようです。 ```scheme (call/cc (lambda (k) ; k は現在の継続 ;; 本体 )) ``` --- ### 2. シンプルな例 ```scheme (define (demo) (call/cc (lambda (go-to-label) (display "Start\n") (if (= (random 3) 0) (begin (display "Jumping to label\n") (go-to-label 'label)) ; ジャンプ (display "Continuing normally\n")) ;; if の後のコード (display "After if\n")))) (demo) ``` * `random` が 0 を返したら、`go-to-label` を呼び出し、継続が捕捉された場所へ戻ります。 * `call/cc` のあとに残るコードは、継続を再度呼び出すまでスキップされます。 --- ### 3. 複数ラベルのシミュレーション ```scheme (define (multi-label-demo) (let ((label-1 #f) (label-2 #f)) ;; ラベル1 (call/cc (lambda (k) (set! label-1 k) (display "At Label 1\n") (if (= (random 2) 0) (begin (display "Jump to Label 2\n") (label-2)) ; ラベル2へジャンプ (display "Stay at Label 1\n"))) (lambda () ;; label‑1 の継続が戻ってきたときに実行 (display "Back at Label 1\n"))) ;; ラベル2 (call/cc (lambda (k) (set! label-2 k) (display "At Label 2\n") (if (= (random 2) 0) (begin (display "Jump to Label 1\n") (label-1)) ; ラベル1へ戻る (display "Stay at Label 2\n"))) (lambda () ;; label‑2 の継続が戻ってきたときに実行 (display "Back at Label 2\n"))))) ``` * 各 `call/cc` は別々の継続を捕捉し、変数に保存して後で呼び出せます。 * 「ラベル」は単なる継続オブジェクトを保持する変数です。 --- ### 4. 実務上のヒント | 問題 | 対処法 | |------|--------| | **無限ループ** | ジャンプが終わらないケースは避け、カウンタで反復回数を制限する。 | | **副作用** | ジャンプ時に既に発生した副作用は保持されるので、可変状態には注意。 | | **可読性** | 継続変数に `goto-label1` など分かりやすい名前を付け、コメントでジャンプ箇所を説明する。 | --- ### 5. 避けるべきケース * 単純な分岐なら `if`, `cond`, ループ構造を使うほうが好ましい。 * 継続の乱用はコードの可読性とデバッグ難易度を下げるため、必要最低限に留めるべきです。 --- **まとめ** `call/cc` を利用して継続を捕捉・再呼び出しすることで、Scheme で `goto` と同等のジャンプ機能を実装できます。強力な手法ですが、可読性と保守性を損なわないように注意して使用してください。 | そっか~ニュース