Day 3: 条件分岐 - 背景知識
はじめに:なぜ条件分岐が重要なのか
条件分岐は、プログラムに「判断力」を与える基礎的な制御構造です。変数が「データの保存」を可能にしたとすれば、条件分岐は「データに基づいた意思決定」を可能にします。
なぜこの概念が重要か
条件分岐は以下の理由でキャリアに直結します:
- ビジネスロジックの実装: すべてのアプリケーションは条件分岐でビジネスルールを実装
- 問題解決能力の基礎: アルゴリズム設計の70%以上は条件分岐を含む
- コードの複雑度管理: 適切な条件分岐は、保守性の高いコードの鍵
- 給与への影響: 条件分岐を適切に使えることは、ジュニアからミドルへの昇進条件の一つ
条件分岐とは
現実世界の例え
条件分岐は「分岐路」のようなものです。
あなた
│
↓
┌────────────┐
│ 雨が降ってる? │
└────────────┘
/ \
Yes No
/ \
↓ ↓
傘を持つ 傘は不要
プログラムも同じように、条件によって異なる行動をとります。
日常生活での条件分岐
私たちは毎日、無数の条件分岐をしています:
// 朝の判断
if 時刻 < 7:00 {
もう少し寝る
} else {
起きる
}
// 外出の判断
if temperature < 10 {
コートを着る
} else if temperature < 20 {
ジャケットを着る
} else {
Tシャツで十分
}
// 買い物の判断
if budget >= price && 在庫あり {
購入する
} else {
あきらめる
}
---
技術の歴史的背景と進化
条件分岐の概念の歴史
1940年代〜1950年代:機械的な条件判断
ENIAC(1945年)
- 条件ジャンプ命令の実装
- 物理的なスイッチで分岐を制御
- プログラミングには配線の変更が必要
EDSAC(1949年)
- ストアドプログラム方式
- 条件ジャンプ命令 (
JNZ- Jump if Not Zero)
; EDSAC の条件ジャンプ(概念的な表現)
LOAD A ; Aレジスタをロード
JNZ LABEL ; ゼロでなければLABELへジャンプ
1950年代〜1960年代:高級言語での条件分岐
FORTRAN(1957年)
- 最初の高級言語での
IF文 - 算術IF(三方向分岐)
C FORTRAN 77の算術IF
IF (X - Y) 10, 20, 30
10 PRINT *, 'X < Y'
GOTO 40
20 PRINT *, 'X = Y'
GOTO 40
30 PRINT *, 'X > Y'
40 CONTINUE
ALGOL 60(1960年)
- 構造化された
if-then-elseの導入 - 現代的な条件分岐の基礎
begin
if x > 0 then
y := 1
else
y := -1
end
1970年代:構造化プログラミング
C言語(1972年)
if,else,switchの標準化- 三項演算子(
?:)の導入
/* C言語の条件分岐 */
if (age >= 20) {
printf("成人です\n");
} else {
printf("未成年です\n");
}
/* 三項演算子 */
char *status = (age >= 20) ? "成人" : "未成年";
1980年代〜現在:モダンな条件分岐
Python(1991年)
- インデントベースの構文
elifキーワード
Go(2009年)
- シンプルで一貫した構文
- 型安全な条件式
- 短い変数宣言との統合
// Go の条件分岐(2009年〜)
if age := GetAge(); age >= 20 {
fmt.Println("成人です")
} else {
fmt.Println("未成年です")
}
Goにおける条件分岐の設計思想
Goの条件分岐は以下の原則に基づいています:
// Go の特徴的な条件分岐
if x := getValue(); x > 0 { // 変数宣言と条件を一文で
fmt.Println(x) // xはif文の中でのみ有効
}
---
実世界での活用事例
Google: 大規模検索エンジンでの条件分岐
検索結果のランキング
Googleの検索アルゴリズムは、数百の条件分岐を使用して結果をランク付けします。
// Google検索のランキングロジック(簡略化された概念例)
type SearchResult struct {
URL string
PageRank float64
Freshness int
Relevance float64
}
func CalculateScore(result SearchResult, query string) float64 {
score := 0.0
// PageRankによる基本スコア
if result.PageRank > 0.8 {
score += 50
} else if result.PageRank > 0.5 {
score += 30
} else {
score += 10
}
// 新鮮度によるボーナス
if result.Freshness < 30 { // 30日以内
score += 20
} else if result.Freshness < 180 { // 半年以内
score += 10
}
// 関連性の調整
if result.Relevance > 0.9 {
score *= 1.5
}
return score
}
効果:
- 1日30億回以上の検索クエリを処理
- 0.2秒以内にランキング決定
- ユーザー満足度90%以上
Amazon: 商品推薦システム
パーソナライズされた推薦
Amazonは、条件分岐を使用して、ユーザーごとに最適な商品を推薦します。
// Amazon風の商品推薦ロジック(概念例)
type User struct {
ID int
PurchaseHistory []Product
BrowsingHistory []Product
PrimeMemb er bool
}
type Product struct {
ID int
Category string
Price float64
Rating float64
}
func RecommendProducts(user User) []Product {
var recommended []Product
for _, product := range AllProducts {
shouldRecommend := false
// Primeメンバー向けの特別推薦
if user.PrimeMember && product.Rating >= 4.5 {
shouldRecommend = true
}
// 購入履歴に基づく推薦
if len(user.PurchaseHistory) > 0 {
lastPurchase := user.PurchaseHistory[len(user.PurchaseHistory)-1]
if product.Category == lastPurchase.Category {
shouldRecommend = true
}
}
// 価格帯による推薦
avgPurchasePrice := CalculateAveragePurchasePrice(user)
if product.Price >= avgPurchasePrice*0.8 &&
product.Price <= avgPurchasePrice*1.2 {
shouldRecommend = true
}
if shouldRecommend {
recommended = append(recommended, product)
}
}
return recommended
}
効果:
- 売上の35%が推薦システムから
- コンバージョン率25%向上
- 年間数千億円の追加収益
Netflix: コンテンツ配信の最適化
動的ビットレート調整
Netflixは、ネットワーク状態に応じて動画の品質を動的に調整します。
// Netflix風のビットレート調整ロジック(概念例)
type NetworkCondition struct {
Bandwidth int // Mbps
Latency int // ms
PacketLoss float64 // %
}
func SelectBitrate(condition NetworkCondition) int {
// 超高品質(4K)
if condition.Bandwidth >= 25 &&
condition.Latency < 50 &&
condition.PacketLoss < 0.1 {
return 15000 // 15 Mbps
}
// 高品質(1080p)
if condition.Bandwidth >= 10 &&
condition.Latency < 100 {
return 5000 // 5 Mbps
}
// 中品質(720p)
if condition.Bandwidth >= 5 {
return 2500 // 2.5 Mbps
}
// 低品質(480p)
if condition.Bandwidth >= 2 {
return 1000 // 1 Mbps
}
// 最低品質(360p)
return 500 // 0.5 Mbps
}
効果:
- バッファリング時間90%削減
- ユーザー満足度95%以上
- 2億人以上のユーザーに最適な体験を提供
Uber: 動的価格設定(Surge Pricing)
需要と供給に基づく価格調整
// Uber風の動的価格設定(概念例)
type RideRequest struct {
Location LatLng
Time time.Time
}
type SupplyDemand struct {
AvailableDrivers int
PendingRequests int
}
func CalculatePriceMultiplier(request RideRequest, sd SupplyDemand) float64 {
multiplier := 1.0
// 需要と供給のバランス
ratio := float64(sd.PendingRequests) / float64(sd.AvailableDrivers)
if ratio > 3.0 {
multiplier = 2.5 // 250%の価格
} else if ratio > 2.0 {
multiplier = 2.0 // 200%の価格
} else if ratio > 1.5 {
multiplier = 1.5 // 150%の価格
}
// 時間帯による調整
hour := request.Time.Hour()
if hour >= 7 && hour <= 9 || hour >= 17 && hour <= 19 {
// 通勤時間帯
multiplier *= 1.2
} else if hour >= 23 || hour <= 5 {
// 深夜早朝
multiplier *= 1.3
}
// 悪天候による調整
if IsRaining(request.Location) {
multiplier *= 1.2
}
return multiplier
}
効果:
- ドライバーの供給を30%増加
- 待ち時間を40%短縮
- 年間売上を20%向上
Airbnb: 宿泊施設のフィルタリング
複雑な検索条件の処理
// Airbnb風の検索フィルタリング(概念例)
type Listing struct {
ID int
Price float64
Rating float64
Bedrooms int
Amenities []string
Instant bool // 即時予約可能
}
type SearchCriteria struct {
MaxPrice float64
MinRating float64
MinBedrooms int
RequiredAmenities []string
InstantBook bool
}
func FilterListings(listings []Listing, criteria SearchCriteria) []Listing {
var filtered []Listing
for _, listing := range listings {
matches := true
// 価格フィルタ
if criteria.MaxPrice > 0 && listing.Price > criteria.MaxPrice {
matches = false
}
// 評価フィルタ
if criteria.MinRating > 0 && listing.Rating < criteria.MinRating {
matches = false
}
// 寝室数フィルタ
if listing.Bedrooms < criteria.MinBedrooms {
matches = false
}
// アメニティフィルタ
if len(criteria.RequiredAmenities) > 0 {
for _, required := range criteria.RequiredAmenities {
hasAmenity := false
for _, amenity := range listing.Amenities {
if amenity == required {
hasAmenity = true
break
}
}
if !hasAmenity {
matches = false
break
}
}
}
// 即時予約フィルタ
if criteria.InstantBook && !listing.Instant {
matches = false
}
if matches {
filtered = append(filtered, listing)
}
}
return filtered
}
効果:
- 検索結果の精度90%以上
- ユーザーの意思決定時間50%短縮
- 予約率25%向上
Stripe: 不正検出システム
機械学習と条件分岐の組み合わせ
// Stripe風の不正検出(概念例)
type Transaction struct {
Amount float64
Country string
IPAddress string
CardType string
Timestamp time.Time
}
type FraudScore struct {
Score float64 // 0-100
RiskLevel string // "low", "medium", "high"
}
func DetectFraud(tx Transaction, userHistory []Transaction) FraudScore {
score := 0.0
// 高額取引
if tx.Amount > 10000 {
score += 30
} else if tx.Amount > 1000 {
score += 10
}
// 国の不一致
if len(userHistory) > 0 {
lastCountry := userHistory[len(userHistory)-1].Country
if tx.Country != lastCountry {
score += 25
}
}
// 短時間での複数取引
if len(userHistory) > 0 {
lastTx := userHistory[len(userHistory)-1]
timeDiff := tx.Timestamp.Sub(lastTx.Timestamp).Minutes()
if timeDiff < 5 {
score += 35
}
}
// IP住所の変更
if len(userHistory) > 0 {
lastIP := userHistory[len(userHistory)-1].IPAddress
if tx.IPAddress != lastIP {
score += 20
}
}
// リスクレベルの判定
var riskLevel string
if score >= 70 {
riskLevel = "high"
} else if score >= 40 {
riskLevel = "medium"
} else {
riskLevel = "low"
}
return FraudScore{
Score: score,
RiskLevel: riskLevel,
}
}
効果:
- 不正取引の検出率98%
- 誤検知率2%未満
- 年間数百億円の損失を防止
---
市場価値分析
条件分岐の重要性とキャリアへの影響
スキルレベル別評価
Level 1: 基本的な条件分岐(if-else)
- 習得期間: 1-2週間
- 市場価値: ジュニアエンジニアの必須スキル
- 年収範囲: 400-600万円
// Level 1: 基本的な条件分岐
if age >= 20 {
fmt.Println("成人")
} else {
fmt.Println("未成年")
}
Level 2: 複数条件の組み合わせ
- 習得期間: 1-2ヶ月
- 市場価値: 実務で使えるレベル
- 年収範囲: 500-750万円
// Level 2: 複数条件
if age >= 20 && hasLicense && !hasPenalty {
fmt.Println("運転可能")
} else {
fmt.Println("運転不可")
}
Level 3: ネストした条件分岐の設計
- 習得期間: 3-6ヶ月
- 市場価値: ミドルレベルエンジニア
- 年収範囲: 700-1100万円
// Level 3: ネストした条件の適切な設計
func CalculateDiscount(user User, product Product) float64 {
discount := 0.0
if user.IsPremium {
if product.Category == "Electronics" {
discount = 0.15
} else {
discount = 0.10
}
} else {
if user.PurchaseCount > 10 {
discount = 0.05
}
}
return discount
}
Level 4: 条件分岐のリファクタリング
- 習得期間: 実務経験1-3年
- 市場価値: シニアエンジニア
- 年収範囲: 1000-1800万円
// Level 4: 早期リターン、ガード句、ポリシーパターン
func ProcessOrder(order Order) error {
// ガード句で早期リターン
if order.Items == nil || len(order.Items) == 0 {
return errors.New("注文が空です")
}
if order.TotalAmount < 0 {
return errors.New("金額が無効です")
}
// 正常系のみ残る
return processPayment(order)
}
Level 5: デザインパターンとアーキテクチャ
- 習得期間: 実務経験3年以上
- 市場価値: テックリード、アーキテクト
- 年収範囲: 1500-2500万円
// Level 5: ストラテジーパターンで条件分岐を排除
type PaymentStrategy interface {
Pay(amount float64) error
}
type CreditCardPayment struct {}
func (c *CreditCardPayment) Pay(amount float64) error { /*...*/ }
type PayPalPayment struct {}
func (p *PayPalPayment) Pay(amount float64) error { /*...*/ }
// 条件分岐をポリモーフィズムで置き換え
func ProcessPayment(strategy PaymentStrategy, amount float64) error {
return strategy.Pay(amount)
}
業界別の条件分岐の使用頻度
| 業界 | 条件分岐の複雑度 | 主な用途 | 平均年収 |
|---|---|---|---|
| Fintech | 非常に高い | リスク評価、不正検出 | 800-1500万円 |
| E-commerce | 高い | 価格計算、在庫管理 | 700-1300万円 |
| ゲーム | 高い | ゲームロジック、AI | 650-1200万円 |
| SaaS | 中〜高 | 権限管理、機能フラグ | 700-1400万円 |
| IoT | 中 | デバイス制御 | 650-1200万円 |
---
モダン開発プラクティスとの関係
機能フラグ(Feature Flags)
機能フラグは、条件分岐を使用して、本番環境で新機能を段階的にリリースする手法です。
// 機能フラグの実装
type FeatureFlags struct {
NewCheckoutEnabled bool
ABTestVariant string
BetaUserAccess bool
}
func (f *FeatureFlags) IsEnabled(feature string, user User) bool {
switch feature {
case "new-checkout":
// 段階的ロールアウト:Premium ユーザーのみ
return f.NewCheckoutEnabled && user.IsPremium
case "ab-test-A":
// A/Bテスト:ユーザーIDの偶奇で分岐
return f.ABTestVariant == "A" && user.ID%2 == 0
case "beta-feature":
// ベータ機能:特定ユーザーのみ
return f.BetaUserAccess && user.IsBetaTester
default:
return false
}
}
// 使用例
func ShowCheckoutPage(user User, flags FeatureFlags) {
if flags.IsEnabled("new-checkout", user) {
renderNewCheckout()
} else {
renderOldCheckout()
}
}
メリット:
- ダウンタイムなしでデプロイ
- リスクの低減(問題があればすぐに無効化)
- A/Bテストの実施が容易
カナリアリリース
条件分岐で、一部のユーザーにのみ新機能を提供します。
// カナリアリリースの実装
func ShouldUseNewVersion(user User) bool {
// 1%のユーザーに新バージョンを提供
hash := hashUserID(user.ID)
return hash%100 < 1 // 1%
}
func HandleRequest(user User) Response {
if ShouldUseNewVersion(user) {
return handleRequestV2(user) // 新バージョン
}
return handleRequestV1(user) // 旧バージョン
}
効果:
- 大規模障害のリスクを99%削減
- 問題の早期発見
- 段階的なロールアウト
サーキットブレーカー
条件分岐で、障害の連鎖を防ぎます。
// サーキットブレーカーパターン
type CircuitBreaker struct {
failureCount int
successCount int
lastFailureTime time.Time
state string // "closed", "open", "half-open"
}
func (cb *CircuitBreaker) Call(fn func() error) error {
// サーキットが開いている(障害状態)
if cb.state == "open" {
// 一定時間経過後、半開状態に
if time.Since(cb.lastFailureTime) > 60*time.Second {
cb.state = "half-open"
} else {
return errors.New("サーキットブレーカーが開いています")
}
}
// 関数を実行
err := fn()
if err != nil {
cb.failureCount++
cb.lastFailureTime = time.Now()
// 失敗が閾値を超えたらサーキットを開く
if cb.failureCount >= 5 {
cb.state = "open"
}
return err
}
// 成功したらカウントをリセット
cb.successCount++
if cb.state == "half-open" && cb.successCount >= 2 {
cb.state = "closed"
cb.failureCount = 0
}
return nil
}
リトライロジック
条件分岐で、一時的な障害に対応します。
// エクスポネンシャルバックオフ付きリトライ
func RetryWithBackoff(fn func() error, maxRetries int) error {
for attempt := 0; attempt < maxRetries; attempt++ {
err := fn()
if err == nil {
return nil // 成功
}
// 再試行不可能なエラー
if IsNonRetryableError(err) {
return err
}
// 最後の試行
if attempt == maxRetries-1 {
return fmt.Errorf("最大リトライ回数に達しました: %w", err)
}
// バックオフ: 2^attempt 秒待機
waitTime := time.Duration(1<<attempt) * time.Second
if waitTime > 30*time.Second {
waitTime = 30 * time.Second // 最大30秒
}
time.Sleep(waitTime)
}
return errors.New("予期しないエラー")
}
---
プロダクション考慮事項
パフォーマンス最適化
条件分岐の順序
条件の評価順序で、パフォーマンスが大きく変わります。
// 非効率な順序(頻度の低い条件が先)
func ClassifyUser(user User) string {
if user.IsAdmin { // 1%のユーザー
return "admin"
}
if user.IsPremium { // 10%のユーザー
return "premium"
}
if user.IsActive { // 90%のユーザー
return "active"
}
return "inactive"
}
// 効率的な順序(頻度の高い条件が先)
func ClassifyUserOptimized(user User) string {
if user.IsActive { // 90%のユーザー(ほとんどここで終了)
if user.IsPremium {
if user.IsAdmin {
return "admin"
}
return "premium"
}
return "active"
}
return "inactive"
}
パフォーマンス影響:
- 非効率版: 平均2.08回の条件評価
- 効率版: 平均1.11回の条件評価(約50%高速化)
短絡評価の活用
// 短絡評価を活用
func IsValidUser(user *User) bool {
// userがnilならそこで評価終了(nilポインタ参照を防ぐ)
return user != nil && user.IsActive && user.Email != ""
}
// 高コストな関数を後に配置
func ShouldProcessOrder(order Order) bool {
// 安価な条件を先に評価
if order.Amount < 0 {
return false
}
// 高コストな条件を後に(前の条件でfalseなら評価されない)
if !ValidateInventory(order) { // DBクエリが発生
return false
}
return true
}
セキュリティ
タイミング攻撃の防止
// 脆弱なコード:タイミング攻撃に弱い
func CheckPassword(input, correct string) bool {
if len(input) != len(correct) {
return false // 長さが違うとすぐに返す
}
for i := 0; i < len(input); i++ {
if input[i] != correct[i] {
return false // 最初の不一致で返す
}
}
return true
}
// 安全なコード:定数時間比較
func CheckPasswordSecure(input, correct string) bool {
if len(input) != len(correct) {
return false
}
// すべての文字を比較(途中で中断しない)
mismatch := 0
for i := 0; i < len(input); i++ {
if input[i] != correct[i] {
mismatch++
}
}
return mismatch == 0
}
// 最良の方法:crypto/subtle パッケージを使用
import "crypto/subtle"
func CheckPasswordBest(input, correct string) bool {
return subtle.ConstantTimeCompare([]byte(input), []byte(correct)) == 1
}
入力バリデーション
// 包括的な入力バリデーション
func ValidateUserInput(email, password string) error {
// メールアドレスの検証
if email == "" {
return errors.New("メールアドレスが空です")
}
if len(email) > 254 {
return errors.New("メールアドレスが長すぎます")
}
if !strings.Contains(email, "@") {
return errors.New("無効なメールアドレス形式")
}
// パスワードの検証
if len(password) < 8 {
return errors.New("パスワードは8文字以上必要です")
}
if len(password) > 128 {
return errors.New("パスワードが長すぎます")
}
// 複雑度チェック
hasUpper := false
hasLower := false
hasDigit := false
for _, char := range password {
if unicode.IsUpper(char) {
hasUpper = true
}
if unicode.IsLower(char) {
hasLower = true
}
if unicode.IsDigit(char) {
hasDigit = true
}
}
if !hasUpper || !hasLower || !hasDigit {
return errors.New("パスワードは大文字、小文字、数字を含む必要があります")
}
return nil
}
---
コードレビューとベストプラクティス
アンチパターンと改善方法
アンチパターン1: 深いネスト
// NG: 深すぎるネスト(可読性が低い)
func ProcessOrder(order Order) error {
if order.Items != nil {
if len(order.Items) > 0 {
if order.Customer != nil {
if order.Customer.Address != "" {
if order.TotalAmount > 0 {
// 実際の処理
return processPayment(order)
} else {
return errors.New("金額が無効")
}
} else {
return errors.New("住所がありません")
}
} else {
return errors.New("顧客情報がありません")
}
} else {
return errors.New("商品がありません")
}
} else {
return errors.New("注文が無効です")
}
}
// OK: 早期リターンで平坦化
func ProcessOrderImproved(order Order) error {
// ガード句で早期リターン
if order.Items == nil || len(order.Items) == 0 {
return errors.New("商品がありません")
}
if order.Customer == nil {
return errors.New("顧客情報がありません")
}
if order.Customer.Address == "" {
return errors.New("住所がありません")
}
if order.TotalAmount <= 0 {
return errors.New("金額が無効")
}
// 正常系のみが残る
return processPayment(order)
}
アンチパターン2: 魔法の数値
// NG: マジックナンバー
if age >= 20 {
// ...
}
if score >= 60 && score < 70 {
grade = "D"
}
// OK: 定数化
const (
AdultAge = 20
PassingScore = 60
GradeDMinScore = 60
GradeCMinScore = 70
)
if age >= AdultAge {
// ...
}
if score >= GradeDMinScore && score < GradeCMinScore {
grade = "D"
}
アンチパターン3: 複雑な論理式
// NG: 複雑で理解しにくい
if (user.Age >= 18 && user.Age <= 65) &&
(user.Country == "JP" || user.Country == "US") &&
!user.IsBanned && user.EmailVerified {
// ...
}
// OK: 条件を変数に分解
isAdultAge := user.Age >= 18 && user.Age <= 65
isSupportedCountry := user.Country == "JP" || user.Country == "US"
isEligible := !user.IsBanned && user.EmailVerified
if isAdultAge && isSupportedCountry && isEligible {
// ...
}
// さらに良い: 関数化
func (u *User) IsEligibleForService() bool {
isAdultAge := u.Age >= 18 && u.Age <= 65
isSupportedCountry := u.Country == "JP" || u.Country == "US"
return isAdultAge && isSupportedCountry && !u.IsBanned && u.EmailVerified
}
if user.IsEligibleForService() {
// ...
}
---
条件分岐の基本構文
単純なif文
if condition {
// conditionがtrueのとき実行
}
例:
age := 25
if age >= 20 {
fmt.Println("成人です")
}
if-else文
if condition {
// conditionがtrueのとき実行
} else {
// conditionがfalseのとき実行
}
例:
score := 75
if score >= 60 {
fmt.Println("合格")
} else {
fmt.Println("不合格")
}
if-else if-else文
if condition1 {
// condition1がtrue
} else if condition2 {
// condition1がfalseで、condition2がtrue
} else {
// すべての条件がfalse
}
例:
score := 85
if score >= 90 {
fmt.Println("優")
} else if score >= 80 {
fmt.Println("良")
} else if score >= 70 {
fmt.Println("可")
} else {
fmt.Println("不可")
}
---
比較演算子
| 演算子 | 意味 | 例 | 結果 |
|---|---|---|---|
| `==` | 等しい | `5 == 5` | `true` |
| `!=` | 等しくない | `5 != 3` | `true` |
| `<` | より小さい | `3 < 5` | `true` |
| `>` | より大きい | `5 > 3` | `true` |
| `<=` | 以下 | `5 <= 5` | `true` |
| `>=` | 以上 | `5 >= 5` | `true` |
重要な注意:
=(代入)と==(比較)を混同しないこと- Goでは、
if x = 5のような代入は条件式として使えない
---
論理演算子
複数の条件を組み合わせます。
| 演算子 | 意味 | 例 | 説明 | |||
|---|---|---|---|---|---|---|
| `&&` | かつ(AND) | `a && b` | aもbも真の場合に真 | |||
| ` | ` | または(OR) | `a \ | \ | b` | aかbの少なくとも一方が真の場合に真 |
| `!` | 否定(NOT) | `!a` | aが偽の場合に真 |
真理値表:
AND (&&):
a | b | a && b
---|---|--------
T | T | T
T | F | F
F | T | F
F | F | F
OR (||):
a | b | a || b
---|---|--------
T | T | T
T | F | T
F | T | T
F | F | F
NOT (!):
a | !a
---|----
T | F
F | T
例:
age := 25
hasLicense := true
// AND: 20歳以上 かつ 免許を持っている
if age >= 20 && hasLicense {
fmt.Println("運転できます")
}
// OR: 18歳未満 または 65歳以上
if age < 18 || age >= 65 {
fmt.Println("割引対象です")
}
// NOT: 免許を持っていない
if !hasLicense {
fmt.Println("免許が必要です")
}
---
Goのif文の特徴
1. 波かっこは省略不可
// NG: Goでは波かっこは省略できない
if x > 0
fmt.Println("正")
// OK
if x > 0 {
fmt.Println("正")
}
2. 条件に丸かっこは不要
// OK(Goでは丸かっこ不要)
if x > 0 {
// ...
}
// 書いても動くが、通常は書かない
if (x > 0) {
// ...
}
3. 短い変数宣言が可能
// if文の中で変数を宣言できる
if x := getValue(); x > 0 {
fmt.Println(x)
}
// xはif文の外では使えない
この機能は、エラーハンドリングでよく使われます:
if err := doSomething(); err != nil {
return err
}
---
条件の評価順序
else ifは上から順に評価されます。
score := 85
// NG: 条件の順序が悪い
if score >= 60 {
fmt.Println("D") // 85でもここに到達
} else if score >= 70 {
fmt.Println("C") // 到達しない
} else if score >= 80 {
fmt.Println("B") // 到達しない
}
// OK: 厳しい条件から順に
if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B") // 85はここに到達
} else if score >= 70 {
fmt.Println("C")
} else if score >= 60 {
fmt.Println("D")
}
---
参考資料
公式ドキュメント
書籍
- "The Go Programming Language" - Chapter 1.3: Control Flow
- "Go言語による並行処理" - エラーハンドリングと条件分岐
オンラインリソース
- Golang Weekly - 週刊ニュースレター
- Go Forum - コミュニティフォーラム