**VisiCalc 再構築**

2026/03/17 19:39

**VisiCalc 再構築**

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

要約

日本語訳:

記事は、1979年のオリジナルが「キラーアプリ」と呼ばれた主要なスプレッドシート機能に焦点を当てつつ、C言語で最小限のVisiCalcクローンを再構築する方法について説明しています。まず、Dan Bricklin と Bob Frankston が16 KB Apple‑II マシン向けに約3,000行の6502アセンブリで作成した VisuCalc の簡潔な歴史と、その売上が100万部を突破したことを紹介します。

クローンの データモデル

セル内容は

enum { EMPTY, NUM, LABEL, FORMULA }
を使って保存し、デフォルトのグリッドは 26 列(A–Z) × 50 行です。再帰下降パーサーが数値、セル参照、関数 (
@SUM
,
@ABS
,
@INT
,
@SQRT
) を処理し、括弧と基本算術演算 (+ − * /) も扱います。依存関係は、グリッド全体を最大で100回まで再計算し、値に変化がなくなるまで繰り返すことで解決します。これは VisiCalc の手動「recalc」コマンドと同様です。

UI

ncurses
で構築され、4つの垂直領域(ステータスバー、編集行、列ヘッダー、グリッド)にレイアウトされています。ユーザーは矢印キーで移動し、スクロール時にはカーソルを中心に表示が保たれます。プログラムはモーダルモード (
READY
,
ENTRY
,
GOTO
) で動作します。
READY
モードでは
/B
(空白)、
/Q
(終了)、
/F
(フォーマット)、
>
(移動)などのコマンドが利用可能です。任意の印刷可能文字は編集を開始します。

実装全体は 500 行未満の C コードで、ファイル I/O、大きなグリッドサイズ、高度な範囲関数、および列/行ロック機能は省略されています。そのため、スプレッドシート内部構造を学習したい開発者やホビイストにとって、軽量で扱いやすい学習ツールとなっています。

参考リンク

  • GitHub の最小限の VisiCalc 実装(単一の gist)
  • より完全な再実装 (
    kalk
    ) へのリンク

本文

VisiCalc:数行のCで作るオリジナルスプレッドシート


概要

  • VisiCalc は約半世紀にわたり世界を支配した。
  • その UX は極めてシンプルで学習コストが低く、データ操作・ロジック記述・結果可視化・アート作成・GameBoy のゲーム実行までこなせる数少ないツールの一つ。

歴史

  • 1979年:Dan Bricklin と Bob Frankston が VisiCalc(初代スプレッドシートソフト)を発表。
  • 約3 000 行の6502アセンブリで書かれ、16 KB RAM のマシン上で動作。
  • Apple II の「キラ―アプリ」として1 百万本以上販売され、初期 PC をビジネスツールへと変貌させた。

目的

ゼロから最小限の VisiCalc クローンを再実装する:

  • データモデル
  • 式評価器
  • セル表示用簡易 UI

結果は次のようになる:

CellsLike

1. データモデル

#define MAXIN 128   // 入力文字列最大長

enum { EMPTY, NUM, LABEL, FORMULA };  // セルタイプ

struct cell {
    int type;
    float val;                // 評価済み値
    char text[MAXIN];         // 生入力
};

#define NCOL 26          // 列 A..Z
#define NROW 50          // 行

struct grid {
    struct cell cells[NCOL][NROW];
};

2. 式評価器(再帰下降パーサ)

struct parser { const char *s; int pos; struct grid *g; };

void skipws(struct parser *p) {
    for (; isspace(*p->s); p->s++);
}

/* A1, AA12 などセル参照を解析 */
int ref(const char *s, int *col, int *row) { ... }

/* 数値を解析 */
float number(struct parser *p) { ... }

/* 参照先セルの値を返す */
static float cellval(struct parser *p) { ... }

/* 関数呼び出し:@SUM(A1...B5)、@ABS(-A1)… */
float func(struct parser *p) { ... }

/* プライマリ式(数値・参照・関数・括弧) */
float primary(struct parser *p) {
    skipws(p);
    if (!*p->s) return NAN;
    if (*p->s == '+') { p->s++; return primary(p); }
    if (*p->s == '-') { p->s++; return -primary(p); }
    if (*p->s == '@') { p->s++; return func(p); }
    if (*p->s == '(') {
        p->s++; float v = expr(p);
        skipws(p); if (*p->s != ')') return NAN; p->s++;
        return v;
    }
    if (isdigit(*p->s) || *p->s=='.') return number(p);
    return cellval(p);
}

/* 乗除:factor [*|/ factor]* */
float term(struct parser *p) {
    float l = primary(p);
    for (;;) {
        skipws(p); char op=*p->s;
        if (op!='*' && op!='/') break;
        p->s++; float r=primary(p);
        if (op=='*') l*=r; else if (r==0) return NAN; else l/=r;
    }
    return l;
}

/* 加減:term [+|- term]* */
float expr(struct parser *p) {
    float l = term(p);
    for (;;) {
        skipws(p); char op=*p->s;
        if (op!='+' && op!='-') break;
        p->s++; float r=term(p);
        l = (op=='+') ? l+r : l-r;
    }
    return l;
}

NAN
は浮動小数点演算で自然にエラーを伝搬させる。


3. 再計算

VisiCalc は「シート全体を再評価」する単純戦略を採用していた:

void recalc(struct grid *g) {
    for (int pass=0; pass<100; ++pass) {
        int changed = 0;
        for (int r=0; r<NROW; ++r)
            for (int c=0; c<NCOL; ++c) {
                struct cell *cl=&g->cells[c][r];
                if (cl->type!=FORMULA) continue;
                struct parser p = { cl->text, cl->text, g };
                float v = expr(&p);
                if (v != cl->val) changed = 1;
                cl->val = v;
            }
        if (!changed) break;
    }
}

4. セル設定

struct cell *cell(struct grid *g, int c, int r) {
    return &g->cells[c][r];
}

void setcell(struct grid *g, int c, int r, const char *input) {
    struct cell *cl = cell(g,c,r);
    if (!cl) return;
    if (!*input) { *cl=(struct cell){0}; recalc(g); return; }

    strncpy(cl->text,input,MAXIN-1);

    if (*input=='+'||*input=='-'||*input=='('||*input=='@')
        cl->type=FORMULA;
    else if (isdigit(*input)||*input=='.') {
        char *end; double v=strtod(input,&end);
        cl->type = (*end=='\0')?NUM:LABEL;
        if (cl->type==NUM) cl->val=v;
    } else
        cl->type=LABEL;

    recalc(g);
}

5. サンプル利用

struct grid g={0};
setcell(&g,0,0,"5");
setcell(&g,0,1,"7");
setcell(&g,0,2,"11");
setcell(&g,0,3,"+@SUM(A1...A3)");
assert(g.cells[0][3].val==23.0f);

setcell(&g,0,0,"5");
setcell(&g,0,1,"+A1+5");
setcell(&g,0,2,"+A2+A1");
assert(g.cells[0][3].val==5.0f+10.0f+15.0f);

setcell(&g,0,0,"7");
assert(g.cells[0][3].val==7.0f+12.0f+19.0f);

6. テキストユーザーインタフェース(curses)

#define CW 9   // 列表示幅
#define GW 4   // 行余白幅

int vcols(void) { return (COLS-GW)/CW; }
int vrows(void) { return LINES-4; }

struct grid {
    struct cell cells[NCOL][NROW];
    int cc,cr;   // カーソル
    int vc,vr;   // ビューポート左上
};

ビューポートスクロール

if (g.cc < g.vc)              g.vc = g.cc;
if (g.cc >= g.vc + vcols())  g.vc = g.cc - vcols() + 1;
if (g.cr < g.vr)              g.vr = g.cr;
if (g.cr >= g.vr + vrows())   g.vr = g.cr - vrows() + 1;

描画

static void draw(struct grid *g, int mode, const char *buf) {
    erase();

    /* ステータスバー */
    attron(A_BOLD|A_REVERSE);
    mvprintw(0,0," %c%d", 'A'+g->cc, g->cr+1);
    if (cur->type==FORMULA)
        printw("  %s = %.10g", cur->text, cur->val);
    mvprintw(0,COLS-6, mode==ENTRY?"ENTRY":"READY");
    attroff(A_BOLD|A_REVERSE);

    /* 編集行 */
    if (mode)   mvprintw(1,0,"> %s_",buf);
    else if (cur->type!=EMPTY)
        mvprintw(1,0,"  %s", cur->text);

    /* 列ヘッダー */
    attron(A_BOLD|A_REVERSE);
    for (int c=0; c<vcols(); ++c)
        mvprintw(2,GW+c*CW,"%*c",CW,'A'+g->vc+c);
    attroff(A_BOLD|A_REVERSE);

    /* グリッドセル */
    for (int r=0; r<vrows() && g->vr+r<NROW; ++r) {
        int row=g->vr+r, y=3+r;
        attron(A_REVERSE); mvprintw(y,0,"%*d ",GW-1,row+1); attroff(A_REVERSE);
        for (int c=0; c<vcols() && g->vc+c<NCOL; ++c) {
            int col=g->vc+c;
            struct cell *cl = cell(g,col,row);
            /* cl->val を文字列 fb にフォーマット … */
            if (col==g->cc && row==g->cr)
                attron(A_REVERSE);
            mvprintw(y,GW+c*CW,"%s",fb);
            if (col==g->cc && row==g->cr)
                attroff(A_REVERSE);
        }
    }
}

7. メインループ & モード

VisiCalc はモーダルインタフェースを採用していた:

モードトリガー
READY
通常の移動
/
コマンドモード(例:
/B
,
/Q
,
/F
>
移動モード(アドレス入力)
それ以外セル編集
for (;;) {
    int ch = getch();
    if (mode==ENTRY && ch==10) { /* Enter */ }
    else if (mode==READY && ch=='/') { /* コマンド */ }
    else if (mode==READY && ch=='>') { /* goto */ }
    else { mode=ENTRY; buf[0]=ch; len=1; }
}

setcell()
が入力を式・数値・ラベルのどれに分類するか判断。


8. 未実装項目

  • ファイル I/O
  • /R
    コマンド(コピー)
  • より大きなグリッド / 列幅制御
  • 追加関数/演算子
  • 複雑な範囲操作(移動、コピー)

結論

500 行未満の C で以下を実装できる:

  • セル:タイプ・値・生テキスト
  • :再帰下降パーサでセル参照と基本関数を扱う
  • 再計算:全シート再評価ループ
  • TUI:curses で移動・編集・表示

VisiCalc の核となるアイデア(セル、式、再計算、グリッド)は今日も変わらず有効です。自分だけの実装を作るか、GitHub のより完成度の高い再実装を探してみてください: https://github.com/zserge/kalk

同じ日のほかのニュース

一覧に戻る →

2026/03/21 6:03

**OpenCode – オープンソースAI コーディング エージェント**

## Japanese Translation: **改善された要約** OpenCodeは、プライバシーを最優先にしつつオープンソースで開発されたAIコーディングエージェントです。ターミナル、IDE、またはデスクトップアプリとしてスムーズに動作します。使用されるLLMに応じて自動的に適切なLanguage Server Protocol(LSP)をロードし、同一プロジェクト上で複数のエージェントを同時に起動できるようにします。セッションは簡単なリンクで共有でき、参照やデバッグに利用できます。OpenCodeはGitHub Copilot、ChatGPT Plus/Pro、およびModels.devを通じて75社以上の大規模言語モデルプロバイダー(ローカルモデルも含む)と統合しており、さらに**Zen**というコーディングエージェント向けに特別にテスト・ベンチマークされたAIモデルのキュレートセットを提供します。プロジェクトは120,000件以上のGitHubスター、800人の貢献者、10,000件以上のコミット数を誇り、毎月5百万社以上の開発者に利用されています。またコードやコンテキストデータを保存しないため、プライバシーセンシティブな環境にも適しています。新リリースや機能拡張について情報を受け取りたいユーザーはウェイトリストに登録できます。

2026/03/21 4:16

**Windows 品質への我々の約束**

## Japanese Translation: Microsoft は、ユーザーから報告されたタスクバーの混乱したオプション、Copilot エントリポイントのごちゃごちゃ、侵入的なアップデート、遅いファイルエクスプローラー、騒がしいウィジェット、分断された Insider Program などの課題に対処するため、Windows 11 のユーザビリティ・パフォーマンス・信頼性の一連の更新を展開しています。 主な変更点は次のとおりです: - **タスクバー**: 新しい再配置オプション(上部、左側、右側)とより小さなタスクバーで、パーソナライズ性が向上します。 - **Copilot**: スニッピングツール、フォト、ウィジェット、メモ帳のエントリポイントを削減し、有用な体験に焦点を当てることでアクセスを簡素化しました。 - **Windows Update**: コントロールが拡張されました—設定中にアップデートをスキップでき、長時間停止、再起動またはシャットダウン時にインストールせずに済み、自動再起動/通知の数が減ります。 - **ファイルエクスプローラー**: 起動速度向上、ちらつき軽減、ナビゲーション滑らか化、ファイルタスクパフォーマンスの信頼性向上です。 - **ウィジェット**: デフォルトが静かになり、外観コントロールが改善され、Discover フィードのパーソナライズが向上しました。 - **Insider Program**: チャネル定義を明確化し、機能アクセスを容易にし、ビルド品質を高め、フィードバック可視性とエンゲージメント機会を増やすことで簡素化されました。 - **Feedback Hub**: 提出速度の向上とコミュニティインタラクションのために大幅な再設計が行われました。 - **システムパフォーマンス目標**: Windows のリソース使用量を低減し、メモリフットプリントを削減、アプリケーション、ファイルエクスプローラー、WSL 全体で応答性を改善します。 - **信頼性イニシアチブ**: OSクラッシュ、ドライバー品質、Bluetooth/USB の安定性、カメラ/オーディオ接続、デバイス再起動の一貫性、および月1 回の単一再起動と一時停止オプションを対象にします。 - **Windows Hello**: 顔認証の信頼性向上、指紋サインイン速度の高速化、ROG Xbox Ally X のようなゲーム用ハンドヘルドデバイスでの PIN 設定のセキュリティ強化です。 - **Craft 改善**: スタート/タスクバーの信頼性向上、パーソナライズ拡張、デバイス設定を静かに、ウィジェットの賢さ向上、通知削減、タスクバー・スタート・ファイルエクスプローラー・設定間で一貫した検索機能。 Microsoft は実際のハードウェア上で検証/テストを深化させ、デフォルトのセキュリティ設定を引き上げ、Insider のフィードバックに依存して将来の Windows 11 リリースを導く予定です。その結果として、ユーザーと開発者双方に対し、より柔軟なインターフェイス、スムーズな更新、静かな通知、そして高い信頼性が実現します。

2026/03/21 6:42

**タイトル:** GLP‑1薬を中止すると心筋梗塞と脳卒中のリスクが急増 **主なポイント:** - GLP‑1受容体作動薬(GLP‑1 RA)をやめると、心筋梗塞・脳卒中のリスクが高まります。 - これらの薬を中止した患者は、継続している患者に比べて心血管イベントの発生率が増加する可能性があります。 - 本研究は、GLP‑1 RAを服用している患者の心血管安全性には、投与継続(薬剤遵守)が重要であることを示唆しています。

## Japanese Translation: (以下の文は、元の意味を正確に保持し、構造や専門用語もそのまま維持した日本語訳です。) **改訂された要約:** 研究によると、短期間であってもグルカゴン様ペプチド‑1(GLP‑1)薬を中断すると、米国退役軍人の2型糖尿病患者において心臓発作や脳卒中のリスクが増加し、継続使用ではそれらのリスクが低減することが示されました。研究者は33万3000人以上の退役軍人を3年間追跡調査しました:GLP‑1治療を2年間停止したグループは心血管リスクが22%増加し、決して中断しなかった患者は18%リスク減少、再開のみで12%の利益にとどまることが明らかになりました。治療が途切れた際には体重・炎症マーカー・血圧・コレステロールが悪化し、「代謝的ウィップラッシュ」と呼ばれる効果が観察されました。GLP‑1薬はもともと糖尿病のために開発されましたが、現在では腎臓・肝臓・心血管系・関節炎・認知症・依存症などのアウトカムにも有益です。ただし、新規使用者のおよそ半数が早期に中断しています。著者らは、服薬遵守と効果を別々に追跡すべきだと主張し、医療システムには長期的な継続利用を支援するプログラムの構築を求めています。この研究は *BMJ Medicine* にZiyad Al‑Aly(ワシントン大学)によって発表され、心血管保護のためにGLP‑1療法を持続させる重要性を強調しています。