AI Library · 知识库

# 泛型函数:保留查找对象的类型

学习目标:用同一个查找算法处理整数和字符串,区分参数化类型、动态调用和相等能力。

前置知识:循环、列表、返回值;Rust 的 Option 只需理解 Some/None 两个分支。

# 问题与边界

输入有限、非空元素的整数或字符串序列及同类型目标,返回第一个相等元素的位置。重复元素取最左侧;空序列或未命中不是异常。Rust/C++ 用可选位置,其余用 -1 表示未命中;调用方必须先分支再访问元素。本题不定义跨类型数值比较、NaN 或任意对象的深相等。返回位置而不复制元素,适合大型记录查找。

# 关键差异

机制 本题含义 不保证什么
Rust PartialEq、Go comparable 约束函数体可以比较 不保证业务主键相等
Java/Kotlin 泛型 元素与目标类型关联 不阻止推断共同父类型
C++ 模板 实例化时检查比较表达式 C++17 没有本例的显式 concept
TS/Python 类型参数 描述输入关系 不在运行时校验外部数据
JS 动态函数 同一函数处理多类值 不建立静态泛型契约

# 各语言实现

# Rust

成功时预期输出为 "not found\n"(\n 表示换行)。

Rustfn locate<T: PartialEq>(items: &[T], target: &T) -> Option<usize> {
    items.iter().position(|item| item == target)
}
fn main() {
    assert_eq!(locate(&[4, 2, 4], &4), Some(0));
    assert_eq!(locate(&["a", "b"], &"b"), Some(1));
    assert_eq!(locate::<i32>(&[], &4), None);
    match locate(&[4, 2], &9) {
        Some(i) => panic!("unexpected index: {i}"),
        None => println!("not found"),
    }
}

&[T] 借用切片,查找不取得元素所有权。PartialEq 才是调用 == 的依据;泛型参数本身不附送比较能力。Rust 泛型。

# Go

成功时预期输出为 "not found\n"(\n 表示换行)。

Gopackage main
import "fmt"
func locate[T comparable](items []T, target T) int {
    for i, item := range items { if item == target { return i } }
    return -1
}
func main() {
    if locate([]int{4, 2, 4}, 4) != 0 || locate([]string{"a", "b"}, "b") != 1 || locate([]int{}, 4) != -1 { panic("lookup") }
    if i := locate([]int{4, 2}, 9); i == -1 { fmt.Println("not found") } else { panic(i) }
}

comparable 允许 ==,但不能据此对切片内容逐项比较。若以接口类型实例化,动态值可能仍不可比较;本文只用具体整数和字符串。Go comparable。

# Java

成功时预期输出为 "not found\n"(\n 表示换行)。

Javaimport java.util.List;
public class Main {
    static <T> int locate(List<T> items, T target) {
        for (int i = 0; i < items.size(); i++) {
            if (items.get(i).equals(target)) return i;
        }
        return -1;
    }
    public static void main(String[] args) {
        if (locate(List.of(4, 2, 4), 4) != 0 || locate(List.of("a", "b"), "b") != 1 || locate(List.<Integer>of(), 4) != -1) throw new AssertionError();
        int i = locate(List.of(4, 2), 9);
        if (i == -1) System.out.println("not found"); else throw new AssertionError(i);
    }
}

泛型类型参数不能直接用 int,这里使用装箱后的 Integer。equals 而非引用 == 才符合字符串内容查找。Java 泛型方法。

# JavaScript

成功时预期输出为 "not found\n"(\n 表示换行)。

JavaScriptfunction locate(items, target) {
    for (let i = 0; i < items.length; i++) {
        if (items[i] === target) return i;
    }
    return -1;
}
if (locate([4, 2, 4], 4) !== 0 || locate(["a", "b"], "b") !== 1 || locate([], 4) !== -1) throw new Error("lookup");
const i = locate([4, 2], 9);
if (i === -1) console.log("not found"); else throw new Error("unexpected index");

严格相等对本例整数、字符串满足契约,但两个内容相同的独立对象并不严格相等。ECMAScript 严格相等。

# TypeScript

TypeScriptfunction locate<T>(items: readonly T[], target: T): number {
    for (let i = 0; i < items.length; i++) {
        if (items[i] === target) return i;
    }
    return -1;
}
if (locate([4, 2, 4], 4) !== 0 || locate(["a", "b"], "b") !== 1 || locate([], 4) !== -1) throw new Error("lookup");
const i = locate([4, 2], 9);
if (i === -1) console.log("not found"); else throw new Error("unexpected index");

readonly 限制通过此参数修改数组,并不冻结运行时对象;类型参数也不将 === 改成深相等。TypeScript 泛型。

# Kotlin

成功时预期输出为 "not found\n"(\n 表示换行)。

Kotlinfun <T> locate(items: List<T>, target: T): Int {
    for ((i, item) in items.withIndex()) if (item == target) return i
    return -1
}
fun main() {
    check(locate(listOf(4, 2, 4), 4) == 0)
    check(locate(listOf("a", "b"), "b") == 1)
    check(locate(emptyList<Int>(), 4) == -1)
    val i = locate(listOf(4, 2), 9)
    if (i == -1) println("not found") else error("unexpected index")
}

Kotlin 的 == 使用结构相等语义,与 Java 的引用 == 不同。这里没有要求可空元素,但 Kotlin 的相等操作本身能处理 null。Kotlin 相等性。

# Python

成功时预期输出为 "not found\n"(\n 表示换行)。

Pythonfrom typing import Sequence, TypeVar
T = TypeVar("T")
def locate(items: Sequence[T], target: T) -> int:
    for i, item in enumerate(items):
        if item == target:
            return i
    return -1
assert locate([4, 2, 4], 4) == 0
assert locate(["a", "b"], "b") == 1
assert locate([], 4) == -1
i = locate([4, 2], 9)
if i == -1:
    print("not found")
else:
    raise AssertionError(i)

TypeVar 供静态检查器关联输入,不影响解释器执行。Python 中 -1 是有效的倒数索引,所以漏掉未命中分支尤其危险。typing 泛型。

# C++

成功时预期输出为 "not found\n"(\n 表示换行)。

C++#include <cassert>
#include <iostream>
#include <optional>
#include <string>
#include <vector>
template<class T>
std::optional<std::size_t> locate(const std::vector<T>& items, const T& target) {
    for (std::size_t i = 0; i < items.size(); ++i) if (items[i] == target) return i;
    return std::nullopt;
}
int main() {
    assert(locate(std::vector<int>{4, 2, 4}, 4) == 0);
    assert(locate(std::vector<std::string>{"a", "b"}, std::string("b")) == 1);
    assert(!locate(std::vector<int>{}, 4));
    if (auto i = locate(std::vector<int>{4, 2}, 9)) { return 1; }
    else { std::cout << "not found\n"; }
}

const& 避免复制整个容器;std::string("b") 使模板推断的两个 T 一致,而不是要求推断自动把字符数组转换成 string。模板表达式在实例化时接受检查。C++ 工作草案:模板是持续更新资料,不作为 C++17 最低版本证明。

# 迁移误区与适用限制

# 运行与验证

每种语言在独立目录按上节文件名和命令执行。Rust/C++ 的可执行程序在 PowerShell 中用 .\main.exe,POSIX shell 中用 ./main。基线见主题首页,实际执行证据集中记入验证记录。本文给出预期语义与检查,不把编写或阅读核对宣称为运行通过;各程序应由验证流程执行,类型错误反例的文字说明不等于已运行负向编译测试。

# 相关阅读