1. successful transfer reduces sender, increases receiver
  2. insufficient balance leaves accounts unchanged
  3. exact balance transfer leaves sender at 0
  4. transfer amount 0 is no-op
  5. three account scenario, only A and C move
コース一覧
コンピューターサイエンス入門:理論編
残高転送のトランザクション風処理

コンピューターサイエンス入門:理論編

2 進数、論理演算、ビット演算、データ構造、計算量、メモリモデルなど、プログラミングの土台となるコンピュータサイエンスの理論知識を学べるコースです。プログラミングを始めたばかりの学習者や、CS の体系的な土台を作りたい現役エンジニアを対象としています。約 13 時間 (1 日 30 分 × 26 日) で 53 レッスンを修了でき、修了後はコードのパフォーマンスや設計判断の根拠を理解した上で実装できるようになります。

1
2進数とビット演算
01. 2進数から10進数への変換5分
02. 10進数から2進数への変換5分
03. ビットAND演算でフラグ判定5分
04. OR / XOR でフラグを操作する5分
05. popcount で 1 のビット数を数える5分
06. シフト演算で 2 倍 / 半分5分
07. 16 進数から 10 進数への変換5分
08. 第 1 章まとめクイズ5分
2
文字コードと真偽値ロジック
01. 文字 → ASCII コード5分
02. コード → 文字5分
03. UTF-8 のバイト長を返す5分
04. AND / OR の真理値表5分
05. NOT / XOR の真理値表5分
06. 含意 (→) を AND/OR/NOT で表現5分
07. ド・モルガンの法則5分
08. 第2章まとめクイズ5分
3
計算量とBig-O
01. forループの実行回数を返す5分
02. 入れ子ループの実行回数5分
03. 線形探索 (O(n))5分
04. 二分探索 (O(log n))5分
05. バブルソート5分
06. 配列の最大と最小5分
07. 累積和で範囲合計5分
08. 第3章まとめクイズ5分
4
データ構造 (基本)
01. スタック (push/pop) を実装する5分
02. キュー (enqueue / dequeue) を実装する5分
03. カッコのバランス判定 (スタック応用)5分
04. set で重複を除去する5分
05. map で出現回数を数える5分
06. 二数の和 (map で O(n))5分
07. アナグラム判定5分
08. 第 4 章クイズ — データ構造 (基本)5分
5
ファイル / メモリ / IO 概念
01. バイト → KB 変換5分
02. ディスクサイズフォーマット5分
03. キャッシュヒット率5分
04. LRU 簡易シミュレーション5分
05. エンディアンの入れ替え (uint32)5分
06. バッファ範囲チェック5分
07. 第5章まとめクイズ5分
6
ネットワークと符号化
01. Base64 エンコード後の長さ計算5分
02. URL エンコード対象文字数5分
03. IPv4 アドレスを 32bit 整数に変換5分
04. 32bit 整数を IPv4 アドレスに変換5分
05. CIDR プレフィックスからネットマスクを作る5分
06. 単純チェックサム5分
07. 第6章まとめクイズ5分
7
設計と DB 基礎
01. 重複データを 1NF に変換 (フラットなリスト化)5分
02. キーで join (map 利用)5分
03. 残高転送のトランザクション風処理5分
04. CAP の availability 模擬5分
05. URL ルーティング5分
06. 疎結合と密結合 — 依存性注入で設計を変える12分
07. コース総まとめクイズ5分

残高転送のトランザクション風処理

引いたところで落ちると、お金が消える

A の口座から 300 円引いて、B の口座に 300 円足す。送金の処理は、書いてしまえば 2 行です。

Python

accounts["A"] -= 300 # ここまでは実行された # ここでサーバーが再起動した accounts["B"] += 300 # 実行されなかった

この 2 行の間でプロセスが落ちると、A だけが 300 円減った世界が残ります。合わせて 1500 円あったはずのお金が 1200 円になっています。誰も受け取っていない 300 円が、どこにも無い状態です。

バグではありません。2 行とも書き方は正しく、順番も正しい。それでも壊れます。「片方だけ実行された状態」が存在しうる限り、いつか必ず起きます。

だから複数の書き換えは、全部やるか 1 つもやらないかのどちらかに固定します。これがトランザクションの原子性(Atomicity)です。データベースの ACID という 4 文字はこの A から始まり、残る 3 つは一貫性、分離性、永続性ですが、送金でまず効くのは A です。

途中まで書き換えない書き方にする

止まっても壊れないようにする道は 2 つあります。書き換える前の状態を控えておいて失敗したら戻すか、書き換えてよいと分かるまで 1 か所も触らないかです。自分の手で書くなら後者が確実です。

送金してよいかどうかは、引く前に判定できます。残高が足りるかは残高を見れば分かる。分かった時点で分岐して、足りなければ何も触らずに返してしまえば、「引いたのに足せなかった」状態が生まれる余地そのものが消えます。

解説

現場の話 — 本物のデータベースは前者を選んでいます。変更を本体に反映する前に別のログへ書き出しておき、途中で電源が落ちても再起動時にログから復元します。COMMIT が一瞬で終わるように見えて裏で泥臭い仕事をしているのは、この控えを取っているからです。

渡された辞書を書き換えると、呼び出し元まで変わる

もう 1 つ、原子性を静かに壊すものがあります。引数で受け取った辞書を、そのまま書き換えることです。

Python

scores = {"数学": 80, "英語": 70} def add_bonus(table): table["数学"] += 10 return table add_bonus(scores) print(scores) # {'数学': 90, '英語': 70}

返り値を受け取ってもいないのに、元の scores まで変わっています。辞書やオブジェクトは中身そのものが渡されるので、受け取った側の書き換えが呼び出し元に筒抜けになります。

これが混ざると、途中まで書き換えてから「やっぱりやめます」と元の値を返しても、呼び出し元の辞書はもう汚れています。先にコピーを作り、コピーだけを触って返してください。

Python

original = {"数学": 80} copied = dict(original) # JavaScript なら { ...original } copied["数学"] = 100 print(original) # {'数学': 80} 元は無事

dict() やスプレッドで作れるのは 1 段だけのコピーです。今回の口座のように値が数値なら、これで足ります。

やってみよう

transfer(accounts, sender, receiver, amount) を完成させてください。

  1. accounts はコピーしてから触る。渡された辞書そのものは最後まで変えない
  2. sender の残高が amount に足りなければ、引きも足しもせずに返す
  3. 足りるときだけ、sender から引いて receiver に足す

テストの期待値を見ると、送金が成功した場合も失敗した場合も、全口座の合計は入力と同じままです。引く処理と足す処理のどちらか一方だけが走ると、この合計が合わなくなります。

要件

  1. sender の残高が amount 未満なら何も変更せず返す(rollback 相当)
  2. 送金成功時は sender から amount を引き、receiver に amount を足す
  3. 入力 accounts を直接書き換えず、新しい辞書 / オブジェクトを返す

入出力例

transfer({"A":1000,"B":500}, "A", "B", 300) → {"A":700,"B":800} transfer({"A":100,"B":500}, "A", "B", 300) → {"A":100,"B":500} transfer({"A":300,"B":200}, "A", "B", 300) → {"A":0,"B":500} transfer({"A":100,"B":200}, "A", "B", 0) → {"A":100,"B":200} transfer({"A":500,"B":200,"C":100}, "A", "C", 250) → {"A":250,"B":200,"C":350}

ヒント

Python なら `dict(accounts)` で浅いコピーを作ってから操作する

JavaScript なら `const next = { ...accounts };` でスプレッドコピー

残高不足チェックは送金処理の前に行う(成功・失敗で同じ流れにしないこと)

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/05/19·更新 2026/08/26

関連レッスン

  • CAP の availability 模擬

    CAP 定理の Availability(可用性)を、複数ノード上の最新値を多数決で返す関数として実装します。

  • URL ルーティング

    URL パスを `Controller#action` の文字列に振り分けるルーティングを実装し、MVC の「Controller の入口」を体感します。

  • 疎結合と密結合 — 依存性注入で設計を変える

    疎結合と密結合の違いを比喩・図・コードで理解する。依存性注入(DI)で結合度を下げると、テスト容易性と変更耐性が同時に上がる。

  • コース総まとめクイズ

    cs-theory コース全 7 章の応用問題。2 進数からビット演算、計算量、データ構造、ネットワーク、DB 設計まで横断的に振り返ります。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • 処理計算や代入を表す長方形
  • プロセス実行中のプログラムのこと。
  • トランザクション「全部成功 or 全部なかったことに」をまとめる単位
  • データベースデータを構造化して保存する仕組み
  • ACID原子性・一貫性・独立性・永続性の4特性
  • 引数位置引数=順番で渡す。
  • オブジェクトキーと値のペアで構造を作るデータ
  • スプレッド...で配列・オブジェクトを展開する
main.py
学習モード
エディタを読み込んでいます

メモ

残高転送のトランザクション風処理

⌘S で保存