AI Library · 知识库

# 遍历与提前退出:查找首个匹配文件

学习目标:在文件列表的线性扫描中,理解提前退出、短路求值与未命中结果的跨语言表达。

前置知识与范围:循环、切片/数组与条件判断。本篇只遍历已加载的内存字符串列表,不执行真实磁盘 I/O。

# 问题与边界

给定一个文件名字符串列表和一个后缀 ext(如 ".log"):

  1. 按列表原始顺序依次检查,一旦遇到以 ext 结尾的文件名,立即返回该文件名并终止后续遍历。
  2. 若列表为空,或遍历完整列表后仍无任何项匹配,显式返回“未找到”,不得抛出未捕获异常或访问越界内存。

边界用例:

# 关键差异

语言 循环与提前退出 未命中表示 空列表行为
Rust for in 配合 return Some(item) Option<&'a str> 循环不执行,返回 None
Go for _, f := range files 配合 return f, true 多返回值 (string, bool) 循环不执行,返回 "", false
Java 增强 for 配合 return Optional.of(f) Optional<String> 返回 Optional.empty()
JavaScript for...of 配合 return f undefined 或 null 循环不执行,返回 null
TypeScript 沿用 JS 运行时,签名标注 string | null 联合类型 string | null 返回 null
Kotlin for (f in files) 配合 return f 可空类型 String? 循环不执行,返回 null
Python for f in files: 配合 return f Optional[str](None) 循环不执行,返回 None
C++ 范围 for 配合 return f std::optional<std::string_view> 返回 std::nullopt

# 各语言实现

# Rust

Rustfn find_first<'a>(files: &'a [&'a str], ext: &str) -> Option<&'a str> {
    for &file in files {
        if file.ends_with(ext) {
            return Some(file);
        }
    }
    None
}

fn main() {
    let files = ["app.log", "data.csv", "trace.log"];
    assert_eq!(find_first(&files, ".log"), Some("app.log"));
    assert_eq!(find_first(&files, ".txt"), None);
    assert_eq!(find_first(&[], ".log"), None);

    match find_first(&files, ".log") {
        Some(name) => println!("found: {name}"),
        None => println!("not found"),
    }
}

Rust 中借用生命周期 'a 确保返回的字符串切片与入参列表有效绑定;未命中通过 Option::None 表达,迫使调用方在解出值前处理缺失情况。

# Go

Gopackage main

import (
	"fmt"
	"strings"
)

func findFirst(files []string, ext string) (string, bool) {
	for _, f := range files {
		if strings.HasSuffix(f, ext) {
			return f, true
		}
	}
	return "", false
}

func main() {
	files := []string{"app.log", "data.csv", "trace.log"}
	if match, ok := findFirst(files, ".log"); ok {
		if match != "app.log" {
			panic("expected app.log")
		}
		fmt.Printf("found: %s\n", match)
	} else {
		panic("expected match")
	}

	if _, ok := findFirst(files, ".txt"); ok {
		panic("expected not found")
	}
	if _, ok := findFirst(nil, ".log"); ok {
		panic("expected not found for empty")
	}
}

Go 惯用多返回值 (string, bool)(comma-ok 惯用法),不依靠魔数空串 "" 来指示查找失败。参见 Go 循环与多返回值规范。

# Java

Javaimport java.util.List;
import java.util.Optional;

public class Main {
    public static Optional<String> findFirst(List<String> files, String ext) {
        for (String file : files) {
            if (file.endsWith(ext)) {
                return Optional.of(file);
            }
        }
        return Optional.empty();
    }

    public static void main(String[] args) {
        List<String> files = List.of("app.log", "data.csv", "trace.log");
        Optional<String> match = findFirst(files, ".log");
        assert match.isPresent() && "app.log".equals(match.get());
        assert findFirst(files, ".txt").isEmpty();
        assert findFirst(List.of(), ".log").isEmpty();

        match.ifPresentOrElse(
            name -> System.out.println("found: " + name),
            () -> System.out.println("not found")
        );
    }
}

Java 标准库提供 Optional 作为显式容器避免未受保护的 null 访问。

# JavaScript

JavaScriptconst assert = {
  equal: (a, b) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
};

function findFirst(files, ext) {
  for (const file of files) {
    if (file.endsWith(ext)) {
      return file;
    }
  }
  return null;
}

const files = ["app.log", "data.csv", "trace.log"];
assert.equal(findFirst(files, ".log"), "app.log");
assert.equal(findFirst(files, ".txt"), null);
assert.equal(findFirst([], ".log"), null);

const result = findFirst(files, ".log");
if (result !== null) {
  console.log(`found: ${result}`);
} else {
  console.log("not found");
}

JavaScript 中使用 null 显式表示“未命中”。for...of 在条件达成时通过 return 立即退出函数与循环,后续元素不会被遍历。

# TypeScript

TypeScriptconst assert = {
  equal: (a: unknown, b: unknown) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
};

function findFirst(files: readonly string[], ext: string): string | null {
  for (const file of files) {
    if (file.endsWith(ext)) {
      return file;
    }
  }
  return null;
}

const files: readonly string[] = ["app.log", "data.csv", "trace.log"];
assert.equal(findFirst(files, ".log"), "app.log");
assert.equal(findFirst(files, ".txt"), null);
assert.equal(findFirst([], ".log"), null);

const result = findFirst(files, ".log");
if (result !== null) {
  console.log(`found: ${result}`);
} else {
  console.log("not found");
}

TypeScript 返回类型为 string | null,调用方在访问字符串属性前必须进行 result !== null 的类型收窄。

# Kotlin

Kotlinfun findFirst(files: List<String>, ext: String): String? {
    for (file in files) {
        if (file.endsWith(ext)) {
            return file
        }
    }
    return null
}

fun main() {
    val files = listOf("app.log", "data.csv", "trace.log")
    val match = findFirst(files, ".log")
    check(match == "app.log")
    check(findFirst(files, ".txt") == null)
    check(findFirst(emptyList(), ".log") == null)

    if (match != null) {
        println("found: $match")
    } else {
        println("not found")
    }
}

Kotlin 的类型系统原生区分可空引用 String? 与不可空引用 String,在 if (match != null) 块内自动智能转换(smart cast)。

# Python

Pythonfrom typing import Sequence, Optional

def find_first(files: Sequence[str], ext: str) -> Optional[str]:
    for f in files:
        if f.endswith(ext):
            return f
    return None

if __name__ == "__main__":
    files = ["app.log", "data.csv", "trace.log"]
    assert find_first(files, ".log") == "app.log"
    assert find_first(files, ".txt") is None
    assert find_first([], ".log") is None

    result = find_first(files, ".log")
    if result is not None:
        print(f"found: {result}")
    else:
        print("not found")

Python 函数在循环未触发 return 时自然执行末尾的 return None。调用方通过 is not None 进行判断。

# C++

C++#include <iostream>
#include <vector>
#include <string_view>
#include <optional>
#include <cassert>

std::optional<std::string_view> find_first(const std::vector<std::string_view>& files, std::string_view ext) {
    for (const auto& file : files) {
        if (file.size() >= ext.size() && file.compare(file.size() - ext.size(), ext.size(), ext) == 0) {
            return file;
        }
    }
    return std::nullopt;
}

int main() {
    std::vector<std::string_view> files = {"app.log", "data.csv", "trace.log"};
    assert(find_first(files, ".log") == std::optional<std::string_view>{"app.log"});
    assert(find_first(files, ".txt") == std::nullopt);
    assert(find_first({}, ".log") == std::nullopt);

    if (auto match = find_first(files, ".log")) {
        std::cout << "found: " << *match << "\n";
    } else {
        std::cout << "not found\n";
    }
}

C++17 引入了 std::optional,配合 std::string_view 避免多余的堆分配拷贝。

# 迁移误区与适用限制

# 运行与验证

语言与版本基线见示例基线。 所有程序运行后控制台输出均为单行:

纯文本found: app.log

所有断言保证边界内、边界外及空集合均能正确提前短路并返回预期结构。

# 相关阅读