開発者が注意すべき落とし穴
(qouteall.fun)- 開発者がよく陥る直感的ではない落とし穴を整理し、発生しやすいバグの原因を紹介する
- HTML、CSS、Unicode/テキストエンコーディング、浮動小数点、時間など、さまざまな技術で頻発する問題を扱う
- 各言語やフレームワークでの 文法や挙動の微妙な違い によって、誤解やエラーが生じうることを強調する
- 並行性、ネットワーキング、データベース などバックエンドの中核領域で、実運用環境で起こりうる落とし穴を例示して説明する
- 多様なサンプルと参考リンクを通じて、問題状況と解決法、そして予期しない挙動の改善点を案内する
HTMLとCSS
-
Flexbox/Gridにおける min-width のデフォルト値
min-widthはデフォルトでautomin-width: autoはコンテンツサイズによって決まり、flex-shrink、overflow: hidden、width: 0、max-width: 100%より優先される- 推奨:
min-width: 0を明示する
-
CSSにおける横方向と縦方向の違い
width: autoは親の空間を埋めようとし、height: autoはコンテンツに合わせられる- inline、inline-block、float 要素の
width: autoは拡張されない margin: 0 autoは水平方向の中央揃え、margin: auto 0では垂直方向の中央揃えはできない(ただしflex-direction: columnでは垂直中央が可能)- マージンの相殺は縦方向でのみ発生する
writing-mode: vertical-rlなどでレイアウト方向が変わると挙動も反転する
-
Block Formatting Context (BFC)
display: flow-rootで BFC を生成できる(そのほかoverflow: hidden/auto/scroll、display: tableなどでも可能だが副作用がある)- 縦に隣接する兄弟要素のマージンが重なったり、子要素のマージンが親の外へ漏れたりする現象は BFC で防げる
- 親が float 子要素しか含まない場合、高さが 0 に潰れる → BFC で修正可能
borderやpaddingがある場合、マージンの相殺は発生しない
-
Stacking Context
- 新しい stacking context を作る条件
transform、filter、perspective、mask、opacityなどのレンダリング属性position: fixedまたはstickyz-index指定 +absolute/relativeの位置指定z-index指定 + flexbox/grid 内部要素isolation: isolate
- 特徴
z-indexは stacking context の内部でのみ適用されるposition: absolute/fixedの座標は最も近い positioned な祖先を基準にするstickyは stacking context をまたいで動作しないoverflow: visibleでも stacking context によって切り取られるbackground-attachment: fixedは stacking context を基準に配置される
- 新しい stacking context を作る条件
-
ビューポート単位
- モバイルブラウザでは、アドレスバーやナビゲーションバーがスクロール時に画面から消えると
100vhの値が変わる - 最新の解決策:
100dvhを使う
- モバイルブラウザでは、アドレスバーやナビゲーションバーがスクロール時に画面から消えると
-
Absolute Position の基準
position: absoluteは親ではなく、最も近いrelative/absoluteまたは stacking context の祖先を基準にする
-
Blur の挙動
backdrop-filter: blurは周囲の要素を考慮しない
-
Float の無効化
- 親が
flexまたはgridの場合、子要素のfloatは効果がない
- 親が
-
パーセント単位の width/height
- 親のサイズが事前に決まっていないと動作しない(循環参照を避けるため)
-
Inline 要素の特性
display: inlineではwidth、height、margin-top、margin-bottomは無視される
-
Whitespace の扱い
- デフォルトでは HTML の改行は空白として扱われ、連続する空白は 1 つに縮約される
- `` は空白の縮約を防ぐが、先頭と末尾の挙動に癖がある
- 多くの場合、コンテンツ先頭/末尾の空白は無視されるが、`` は例外
inline-block間の空白や改行は実際の間隔として表示される(flex/grid では発生しない)
-
text-align
- テキストおよび inline 要素の整列には適用されるが、block 要素の整列には適用されない
-
box-sizing
- デフォルト値は
content-box→ padding/border は含まれない width: 100%+paddingを設定すると親領域をはみ出す可能性がある- 解決策:
box-sizing: border-box
- デフォルト値は
-
Cumulative Layout Shift
- `` に
widthとheight属性を指定しないと、画像読み込みの遅延によりレイアウトの揺れが発生する - 推奨: 属性を指定して CLS を防ぐ
- `` に
-
Chrome におけるファイルダウンロードのネットワークリクエスト
- DevTools のネットワークパネルには表示されない(別タブで処理される)
- 分析が必要なら
chrome://net-export/を使う
-
HTML 内の JavaScript パース問題
console.log('')のような場合、最初の `` が終了タグとして認識される- 参考: Safe JSON in script tags
Unicodeとテキストエンコーディング
-
コードポイントと書記素クラスター
- 書記素クラスターは GUI における「文字単位」
- 可視 ASCII 文字は、コードポイント 1 個 = 書記素クラスター 1 個
- 絵文字は、複数のコードポイントで構成された 1 つの書記素クラスターである場合がある
- UTF-8 ではコードポイントは 1〜4 バイトで、バイト数とコードポイント数は一致しない
- UTF-16 ではコードポイントは 2 バイトまたは 4 バイト(サロゲートペア)
- 標準ではクラスター内のコードポイント数に制限はないが、実装では性能上の制限がある
-
言語ごとの文字列挙動の違い
- Rust: 内部文字列は UTF-8 を使用し、
len()はバイト数、直接インデックス参照は不可、chars().count()はコードポイント数、UTF-8 の妥当性を厳格に検証する - Golang: 文字列は事実上バイト配列で、長さとインデックス参照はバイト単位、主に UTF-8 を使用する
- Java, C#, JS: UTF-16 ベースで、長さは 2 バイト単位で測定され、インデックス参照も 2 バイト単位、サロゲートペアが存在する
- Python:
len()はコードポイント数を返し、インデックス参照では 1 つのコードポイントを含む文字列を返す - C++:
std::stringにはエンコーディング制約がなく、バイトベクターのように動作し、長さ/インデックス参照はバイト単位 - 挙げられた言語の中で、書記素クラスター単位で長さやインデックス参照を行う言語はない
- Rust: 内部文字列は UTF-8 を使用し、
-
BOM (Byte Order Mark)
- 一部のテキストファイルは BOM を持つ。例: EF BB BF → UTF-8 エンコーディングを示す
- 主に Windows で使われ、Windows 以外のソフトウェアでは BOM を処理できないことがある
-
その他の注意点
- バイナリデータを文字列に変換する際、壊れた部分は � (U+FFFD) に置き換えられる
- Confusable characters が存在する(互いによく似て見える文字)
- 正規化(Normalization): 例) é は U+00E9(単一コードポイント)または U+0065+U+0301(2つのコードポイント)で表現可能
- Zero-width characters および Invisible characters が存在する
- 改行コードの違い: Windows は CRLF
\r\n、Linux/MacOS は LF\n - 漢字統合(Han unification): 言語ごとに字形が少し異なる文字が同じコードポイントを使う
- フォントが言語別の異体字を含めて適切にレンダリングする
- 国際化では正しいフォントバリアントの選択が必要
浮動小数点 (Floating point)
-
NaN の性質
- NaN は自分自身を含むどの値とも等しくない(
NaN == NaNは常に false) NaN != NaNは常に true- NaN を含む演算結果はたいてい NaN として伝播する
- NaN は自分自身を含むどの値とも等しくない(
-
特殊な値
- +Inf と -Inf が存在し、NaN とは異なる
- -0.0 は +0.0 と区別される値
- 比較演算では同一だが、一部の計算では異なる動作をする
- 例:
1.0 / +0.0 == +Inf,1.0 / -0.0 == -Inf
-
JSON との互換性
- JSON 標準では NaN と Inf は許可されない
- JS
JSON.stringifyは NaN、Inf をnullに変換する - Python
json.dumps(...)は NaN、Infinity をそのまま出力する(標準違反)allow_nan=Falseオプション指定時は NaN/Inf があるとValueErrorが発生する
- Golang
json.Marshalは NaN/Inf が存在するとエラーを返す
- JS
- JSON 標準では NaN と Inf は許可されない
-
精度の問題
- 浮動小数点の直接比較は失敗する可能性がある →
abs(a - b) < ε形式を推奨 - JS はすべての数値を浮動小数点として扱う
- 安全な整数範囲は
-(2^53 - 1)~2^53 - 1 - この範囲を外れると整数表現が不正確になる
- 大きな整数には
BigIntの使用を推奨 - JSON に安全範囲を超えた整数が含まれると、
JSON.parseの結果値は不正確になる可能性がある - ミリ秒単位のタイムスタンプは 287,396 年先まで安全だが、ナノ秒単位では問題が発生する
- 安全な整数範囲は
- 浮動小数点の直接比較は失敗する可能性がある →
-
演算法則が成り立たない
- 演算順序による精度損失のため、結合法則や分配法則は厳密には成り立たない
- 並列演算(行列積、総和など)は非決定的な結果を生むことがある
-
性能
- 除算は乗算よりはるかに遅い
- 同じ数で何度も割る場合は、逆数を先に求めて掛ける形に最適化できる
-
ハードウェアによる違い
- FMA(Fused Multiply-Add) 対応の有無: 一部のハードウェアでは中間計算をより高精度で行う
- Subnormal range の処理: 最新ハードウェアは対応しているが、一部の旧世代では 0 として扱う
- 丸めモードの違い
- RNTE(最も近い偶数への丸め)、RTZ(0 方向への切り捨て)などがある
- x86/ARM ではスレッドローカルな mutable 状態として設定可能
- GPU では命令単位で丸めモードが異なる
- 三角関数、対数など数学関数の動作差
- x86 にはレガシーな 80 ビット FPU と per-core rounding mode が存在する → 使用非推奨
- このほかにもさまざまな要因で、ハードウェアごとに浮動小数点の結果が変わりうる
-
精度向上の方法
- 計算グラフを浅く構成する(乗算が連続する構造を減らす)
- 中間値が極端に大きい、または極端に小さいケースを避ける
- FMA のようなハードウェア演算を活用する
時間 (Time)
-
うるう秒(Leap second)
- Unix タイムスタンプはうるう秒を無視する
- うるう秒が発生すると前後の区間で時間を伸縮させることがある(Leap smear)
-
タイムゾーン(Time zone)
- UTC と Unix タイムスタンプは世界共通
- 人間が読む時刻は地域ごとのタイムゾーンに依存する
- DB にはタイムスタンプを保存し、UI で変換する方式を推奨
-
サマータイム(DST)
- 一部地域では夏季に時計を 1 時間調整する
-
NTP 同期
- 同期の過程で時刻が「後戻りする」状況が発生しうる
-
サーバーのタイムゾーン設定
- サーバーは UTC に設定することを推奨
- 分散システムでノードごとにタイムゾーンが異なると問題が発生する
- システムのタイムゾーン変更後は DB の再設定または再起動が必要
-
ハードウェアクロック vs システムクロック
- ハードウェアクロックにはタイムゾーンの概念がない
- Linux: ハードウェアクロックを UTC として扱う
- Windows: ハードウェアクロックをローカル時刻として扱う
Java
==はオブジェクト参照の比較であり、オブジェクト内容の比較には.equalsを使う必要があるequalsとhashcodeをオーバーライドしないと、map/set ではオブジェクト同一性が参照ベースで判断される- map の key オブジェクトや set の要素オブジェクトの内容を変更すると、コンテナの動作が壊れる
Listを返すメソッドは、場合によって mutable なArrayListまたは immutable なCollections.emptyList()を返し、後者を変更するとUnsupportedOperationExceptionが発生するOptionalを返すメソッドがnullを返すことがある(推奨されない)finallyブロックで return すると、tryまたはcatchで発生した例外が無視され、finallyの戻り値が適用される- interrupt を無視するライブラリが存在し、IO を含むクラス初期化の過程が interrupt によって壊れることがある
- スレッドプールで
.submit()に渡した task の例外は、デフォルトではログに出力されず future でしか確認できないため、future を無視すると例外を確認できないscheduleAtFixedRateの処理は例外が発生すると静かに停止する
- 数値リテラルが 0 で始まると 8 進数として扱われる(
0123→ 83) - デバッガはローカル変数の
.toString()を呼び出すため、一部クラスではtoString()に副作用があり、デバッグ時にコードの動作が変わることがある(IDE で無効化可能)
Golang
append()は capacity に余裕があるとメモリを再利用し、subslice への append で親のメモリまで上書きすることがあるdeferは関数の return 時に実行され、ブロックスコープ終了時ではないdeferは mutable な変数をキャプチャするnil関連- nil slice と empty slice は異なる
- string は nil にできず、空文字列のみ存在する
- nil map は読み取りは可能だが書き込みはできない
- interface nil の特殊な動作: data pointer が null でも type info が null でなければ
nilと等しくない
- Dead wait: Go には実際の並行性バグ事例が存在する
- Timeout の種類 は多様で、net/http で詳しく扱われている
C/C++
std::vectorの要素ポインタを保存した後に vector が grow すると再割り当てが発生し、ポインタが無効化される- リテラル文字列から生成された
std::stringは一時オブジェクトである可能性があり、c_str()呼び出し時に危険 - 反復中にコンテナを変更すると iterator の無効化が発生
std::removeは実際の削除ではなく要素の再配置であり、削除にはeraseが必要- 数値リテラルが 0 で始まると 8 進数として扱われる (
0123→ 83) - Undefined behavior (UB): 最適化の過程で UB は自由に変化し得るため、依存すると危険
- 初期化されていないメモリへのアクセスは UB
char*を struct ポインタに変換する場合、オブジェクト寿命の開始前アクセスにより UB となるため、memcpyによる初期化を推奨- 不正なメモリアクセス(null ポインタなど)は UB
- 整数の overflow/underflow は UB(unsigned は 0 未満への underflow が可能)
- Aliasing: 異なる型のポインタが同じメモリを参照すると strict aliasing rule により UB が発生
- 例外: 1) 継承関係にある型 2)
char*,unsigned char*,std::byte*への変換(逆変換には適用されない) - 強制変換には
memcpyまたはstd::bit_castを推奨
- 例外: 1) 継承関係にある型 2)
- Unaligned memory へのアクセスは UB
- メモリ Alignment
- 64 ビット整数はアドレスが 8 で割り切れる必要がある
- ARM では unaligned access でクラッシュする可能性がある
- バイトバッファを struct として直接解釈すると alignment 問題が発生
- alignment は struct padding を生み、メモリの無駄につながることがある
- 一部の SIMD 命令(AVX など)は整列されたデータしか処理できず、通常は 32 バイト alignment が必要
Python
- 関数のデフォルト引数は呼び出しのたびに新しく生成されず、最初の値がそのまま保持される
SQL Databases
-
Null の扱い
x = nullは動作せず、x is nullを使う必要がある- Null は自分自身と等しくない(NaN に似ている)
- Unique index は Null の重複を許容する(ただし Microsoft SQL Server は例外)
select distinctにおける Null の扱いは DB ごとに異なるcount(x)とcount(distinct x)は Null 値がある行を無視する
-
一般的な動作
- 日付の暗黙変換は timezone に依存する場合がある
- 複雑な join + distinct は入れ子クエリより遅い場合がある
- MySQL(InnoDB) で string フィールドが
utf8mb4でない場合、4-byte UTF-8 文字を挿入するとエラーが発生 - MySQL(InnoDB) はデフォルトで 大文字小文字を区別しない
- MySQL(InnoDB) は暗黙変換を許容する:
select '123abc' + 1;→ 124 - MySQL(InnoDB) の gap lock は deadlock を引き起こす可能性がある
- MySQL(InnoDB) では group by と select カラムが一致しない場合、非決定的な結果を返す
- SQLite では
strictでなければフィールド型にはあまり意味がない - Foreign key は暗黙の lock を発生させ、deadlock を引き起こす可能性がある
- Locking は DB によっては repeatable read isolation を破る可能性がある
- 分散 SQL DB は locking をサポートしていない、または特異な動作をする可能性がある(DB ごとに異なる)
-
性能/運用
- N+1 query 問題は各クエリが高速であるため、slow query log には現れない
- 長時間実行されるトランザクションは lock 問題などを引き起こすため、トランザクションは素早く終えることが推奨される
- テーブル全体 lock の事例
- MySQL 8.0+ では unique index/foreign key の追加時、多くの場合は並行処理が可能
- 古い MySQL バージョンではテーブル全体 lock が発生する可能性がある
mysqldumpに--single-transactionオプションがないと、テーブル全体に read lock がかかる- PostgreSQL で
create unique indexやalter table ... add foreign keyはテーブル全体の read lock を引き起こす- 回避策:
create unique index concurrentlyを使用 - foreign key は
... not validの後にvalidate constraintを使う方法を利用
- 回避策:
-
Range クエリ
- 重ならない範囲:
- 単純条件
p >= start and p file 2>&1→ stdout+stderr の両方がファイルへ、cmd 2>&1 > file→ stdout のみファイルへ、stderr はそのまま
- 単純条件
- 重ならない範囲:
-
ファイル名は 大文字小文字を区別 する(Windowsとは異なる)
-
実行ファイルには capability システムが存在する(
getcapで確認) -
未設定変数の危険性:
DIRが unset の場合、rm -rf $DIR/→rm -rf /実行の危険 →set -uで防止可能 -
環境の反映: スクリプトを現在の shell に反映するには
source script.shを使う → 永続的に反映するには~/.bashrcに追加 -
Bash は コマンドをキャッシュする:
$PATH内のファイル移動時にENOENTが発生 →hash -rでキャッシュ更新 -
変数を引用せずに使うと改行が空白として扱われる
-
set -e: スクリプトでエラーが起きると即時終了するが、条件文の内部(||,&&,if)では動作しない -
K8s livenessProbe とデバッガの衝突: ブレークポイントデバッガはアプリ全体を停止させ、health check 応答に失敗 → Pod が終了する可能性がある
React
- レンダリングコードで state を直接変更
- Hook を if/loop の中で使用 → ルール違反
useEffectの dependency array に必要な値が不足useEffectで クリーンアップ(clean up)コードの漏れ- Closure trap: 古い state をキャプチャしてバグが発生
- 間違った場所でデータを変更 → 不純なコンポーネント
useCallbackの使用漏れ → 不要な再レンダリングが発生- メモ化されたコンポーネントに 非メモ化の値を渡す と memo 最適化が無効になる
Git
-
Rebase は履歴の書き換え
- rebase 後に通常の push をすると衝突 → 必ず force push が必要
- remote branch の履歴を変更した場合、pull も
--rebaseを使用 --force-with-leaseは一部のケースで他の開発者の commit の上書きを防げるが、fetch だけして pull していないと保護されない
-
Merge revert の問題
- Merge revert は効果が不完全 → 同じブランチを再度 merge しても変化がない
- 解決策: revert の revert を実行する、またはクリーンな方法(backup → reset → cherry-pick → force push)
-
GitHub 関連の注意事項
- API キーのような secret を commit した後、force push で上書きしても GitHub には記録が残る
- private repo A を fork した B が private でも、A が public になると B の内容も公開される(削除後もアクセス可能)
-
git stash pop: conflict 発生時、stash は drop されない -
.DS_Storeは macOS が自動生成 →.gitignoreに**/.DS_Storeの追加を推奨
Networking
- 一部の ルーター・ファイアウォールはアイドル状態の TCP 接続を静かに切断する → HTTP クライアント・DB クライアントのコネクションプールが無効化される可能性 → 解決策: TCP keepalive を設定
tracerouteの結果は 信頼性が低い → 場合によってはtcptracerouteのほうが有用- TCP slow start はレイテンシ増加の原因 →
tcp_slow_start_after_idleを無効化して解決可能 - TCP sticky packet 問題: Nagle アルゴリズムはパケット送信を遅延させる →
TCP_NODELAYを有効にして解決可能 - Nginx の背後にバックエンドを配置する場合、コネクション再利用の設定が必要 → 未設定だと高負荷環境で内部ポート不足により接続失敗
- Nginx はデフォルトで パケットをバッファリングする → SSE(EventSource) の遅延が発生
- HTTP 標準は GET・DELETE リクエストの body を禁止していない → body を使う例もあるが、多くのライブラリ・サーバーは対応していない
- 1つの IP で複数の Web サイトをホスティング可能 → 振り分けは HTTP
Hostヘッダと TLS の SNI が担当 → 単純な IP アクセスでは表示できないサイトが存在 - CORS: 異なる origin へのリクエスト時、ブラウザはレスポンスへのアクセスを遮断 → サーバー側で
Access-Control-Allow-Originヘッダの設定が必要- Cookie 送信を含む場合は追加設定が必要
- フロントエンドとバックエンドが 同一ドメイン・同一ポート なら CORS の問題はない
Other
-
YAML の注意点
- YAML は 空白に敏感 →
key:valueはエラー、key: valueが正しい - 国コード
NOは引用符なしで書くとfalseと解釈される問題が発生 - Git commit hash を引用符なしで書くと数値に変換される可能性がある
- YAML は 空白に敏感 →
-
Excel の CSV 問題
- Excel は CSV を開くと 自動変換を行う
- 日付変換:
1/2,1-2→2-Jan - 大きな数値の不正確な変換:
12345678901234567890→12345678901234500000
- 日付変換:
- 原因は Excel が内部的に数値を floating point として扱うため
- この問題により、遺伝子名 SEPT1 が誤って変更された事例がある
- Excel は CSV を開くと 自動変換を行う
1件のコメント
Hacker Newsのコメント
一部のルーターやファイアウォールは、アプリケーションに何のシグナルも送らずにアイドル状態のTCP接続を静かに切断してしまうことがある。HTTPクライアントライブラリやデータベースクライアントのようにTCPコネクションプールを維持するコードでは、その結果、何の警告もなく接続が無効化される問題を経験する。これを解決するには、システムのTCP keepaliveを設定するか、HTTPであれば
Connection: keep-alive,Keep-Alive: timeout=30, max=1000ヘッダーを使う方法がある。TCPコネクションは一度接続されると中間ルーターには状態が残らない。問題はファイアウォールやNATのセッションタイムアウトで、このときRSTパケットも来ない。K8s環境ではconntrackモジュールの設定が低すぎた問題に遭遇したことがある。HTTP Keep-Aliveを使っても接続の再利用にしか効果がなく、ネットワーク上の接続そのものを維持するわけではない(説明リンク)。HTTP Keep-Aliveは実際にはパケットを発生させず、単に終了を先延ばしにするだけである。一方、TCP Keep-Aliveは定期的にパケットを発生させてタイマーをリセットするOptional<T>を返すメソッドがnullを返すことがある。こういう慣行はあまりにも混乱を招く。感情的に余裕があれば、@java.lang.NonNullReferenceのようなアノテーションを導入するJEPを出していたと思う。このアノテーションで型を宣言すると、コンパイラがnullの代入をエラー扱いするようにしたい。たとえばAlphaはnullの代入が可能だが、Betaではエラーになるような形で。javacでもdead code eliminationが実際にどうなっているか仕様を見直す必要がありそうだ。たとえばif (true)でb = nullする部分は実際にはelidedされ、法的には許可されるコードなのかもしれないKotlinではすでにこのようなケースはコンパイルエラーになる。わざわざアノテーションは不要である
nullのある言語でOptional<T>をあえて使う必要があるのか疑問だ。Pythonで関数の戻り値をOptionalオブジェクトではなく単にT | Noneと書くのと同じように、結局チェックが必要ならフレームワークとしての差別化が曖昧になる。特別なモナドスタイルを使わない限り、結局チェックは同じであるJava、C#、JSがインメモリ文字列をUTF-16系でエンコードしていると言っていたが、Javaについては誤りであり、C#やJSも同様かもしれない。どの言語でも文字列型が十分に不透明なら、インメモリ表現方式は実装詳細である。Javaでは9以降その点が明確になった(関連JEPリンク)。FFIがある場合に実装詳細の変更が難しい理由でもある。また、JSの数値についてmax accurate integerが
2^53−1だと言われるが、実際には2^100のようなより大きい整数も正確に表現できる。2^53−1というのはn-1,n,n+1がすべてIEEE doubleで正確に表現されるという意味であり、したがってn == n-1,n == n+1はどちらもfalseになるC#はインメモリ表現がかなり固定されている。
ReadOnlySpan<char>や生のchar*でバッファに直接アクセスすることが多いため、charはUTF-16コードポイント型である。JSは何とか回避できるかもしれないmax accurate integerという表現より、max safe integerと言いたい
(Base64エンコーディング関連)Java、C#、JSのインメモリ文字列がUTF-16系ではないという点について、技術的には正しいかもしれないが、もしUTF-8ベースの言語で文字列をBase64エンコードしてからJavaでデコードすると、JavaのUTF-16表現のせいで問題が発生するケースがある
マニュアル形式のTipsや情報は、すでに知っていることやほぼ知っていたことほど理解が早く進むように思える。ほとんどのマニュアルは学習そのものより、整理や復習に効果的で、まったく知らない人に教えるには非効率である
「Traceroute Isn’t Real」(トレースルートは本当ではない)という記事をとても興味深く読んだ。これまでトレースルートのデータが非常に不正確だったり意味がないように見えたりすることを実感していたが、その理由が分かって助かった(原文リンク)。もし最新情報があれば教えてほしい
この記事は実際の落とし穴やトラップというより、筆者が経験的に学んだ小さなTipsを集めたリストである。多くの内容は特定の狭い文脈でしか適用されないことがあるのに、その文脈が明確でなく、一部は誤情報のようにも見える。だから、記事全体をあまり額面どおりに受け取る必要はなく、一種の思考の流れやメモ程度に見るのがよい
Pythonでデフォルト引数の値は呼び出しごとに新しく生成される値ではなく、保存された値だという話は、
datetime変数を使うときにぜひ知っておくべきポイントであるページ最初の「トラップ」で
min-width: autoはコンテンツに応じて最小幅が決まると言っていたが、flex/gridでない場合は実際にはそうではない。MDNによれば、block、inline、tableなどではautoは0に補正される(公式文書リンク)本当の最初のトラップは、「どんなCSSプロパティも切り離して読むことはできない」ということだ。プロパティ名どおりcascading(カスケーディング)であり、デフォルト値と複数のルールの結果がどこかで合成されるので、文書全体の文脈が重要になる
CSSのテキストプロパティ側のcascade(継承)はまだ理解できる。しかしCSSレイアウトは、ページデザイナー、実装者、ユーザー、どの視点から見ても理解するのがあまりに難しい。いったい誰のためのデザインなのか共感できない
全体としては悪くないリスト記事で、いくつか意見がある。
Aとフランス語のAを別々に定義すべきだと言っているようなものだ。実際にはそうではない。Han unification の項目を参照-0.0と+0.0(負の0、正の0)は浮動小数点比較では等しいものとして扱われるが、区別する方法はある。それぞれのビットパターンや、1.0/-0.0 = -∞,1.0/0.0 = +∞という結果で確認できる(low + high) / 2はオーバーフローの危険があるのでlow + (high - low) / 2を使えと言っていたが、lowやhighが負になり得るなら、オーバーフローの範囲を移しているだけかもしれない。一般的な二分探索で重要な問題である両言語の利用者が概念的には同じ文字として認識するとしても、単なる「フォントの変形」程度に片付けてはいけない。Unicodeコードポイントが同じだからといって安全に文字置換できるわけではない。日本のユーザーの立場では、このような置換が製品利用を拒む理由になり得る
実際、英語のAとフランス語のAは区別しないが、
А(キリル文字)とA(ラテン文字)のように見た目が同じでも実際には別のコードポイントとして存在するものがある。Han統合も、外見がかなり異なる文字をひとつにまとめる場合が多く、日本語や中国語の学習者なら実際に混乱を経験しうる。たとえば喝(drink)という字は リンク のように表示によってかなり違って見える。コピーしてみると、その場ですぐ形が変わることさえあり、処理方式がかなり複雑である。Han unificationは現実的にかなり厄介なテーマだnumpyとpytorchの間に微妙な違いがあると言及していたが、具体例の説明なしではあまり有用ではなく、情報も不足しているので、現実的な落とし穴とは感じられない