# 返回多个信息:同时返回最小值与最大值
学习目标:通过同时计算序列的极值,理解原生多返回值、元组、结构体与模式解构在各语言中的表达差异。
前置知识与范围:循环、数组/列表与基本数据结构。只处理内存中的整数序列。
# 问题与边界
给定一个整数序列,遍历一次并同时返回其中的最小值与最大值:
- 正常输入:如
[3, 1, 9, 2],返回min = 1, max = 9。 - 单元素输入:如
[5],返回min = 5, max = 5。 - 空输入边界:由于不存在极值,空序列必须显式拒绝或返回空指示,禁止产生未定义的越界读取或伪造的 0。
# 关键差异
| 语言 | 返回形态 | 调用方解构与接收 | 空集合契约 |
|---|---|---|---|
| Rust | 元组 (i32, i32) 包装在 Option 中 |
let (min, max) = ... |
返回 None |
| Go | 原生多返回值 (int, int, error) |
min, max, err := ... |
返回 0, 0, error |
| Java | 记录类 record MinMax(int min, int max) |
访问器方法 res.min(), res.max() |
抛出 IllegalArgumentException |
| JavaScript | 包含属性的对象 { min, max } 或数组元组 |
对象解构 const { min, max } = ... |
抛出 RangeError |
| TypeScript | 命名属性对象 { readonly min: number; readonly max: number } |
对象解构 | 抛出 RangeError |
| Kotlin | 标准库 Pair<Int, Int> 或数据类 |
结构化解构 val (min, max) = ... |
抛出 NoSuchElementException |
| Python | 原生元组 tuple[int, int] |
序列解构 min_val, max_val = ... |
抛出 ValueError |
| C++ | std::pair<int, int> 配合 std::optional |
C++17 结构化绑定 auto [min, max] = ... |
返回 std::nullopt |
# 各语言实现
# Rust
Rustfn min_max(nums: &[i32]) -> Option<(i32, i32)> {
if nums.is_empty() {
return None;
}
let mut min = nums[0];
let mut max = nums[0];
for &x in &nums[1..] {
if x < min { min = x; }
if x > max { max = x; }
}
Some((min, max))
}
fn main() {
assert_eq!(min_max(&[3, 1, 9, 2]), Some((1, 9)));
assert_eq!(min_max(&[5]), Some((5, 5)));
assert_eq!(min_max(&[]), None);
if let Some((min, max)) = min_max(&[3, 1, 9, 2]) {
println!("min: {min}, max: {max}");
}
}
Rust 的匿名元组 (i32, i32) 无须提前定义类型结构,通过模式匹配轻松解构。参见 Rust 元组类型。
# Go
Gopackage main
import (
"errors"
"fmt"
)
func MinMax(nums []int) (int, int, error) {
if len(nums) == 0 {
return 0, 0, errors.New("nums cannot be empty")
}
minVal, maxVal := nums[0], nums[0]
for _, x := range nums[1:] {
if x < minVal {
minVal = x
}
if x > maxVal {
maxVal = x
}
}
return minVal, maxVal, nil
}
func main() {
minVal, maxVal, err := MinMax([]int{3, 1, 9, 2})
if err != nil || minVal != 1 || maxVal != 9 {
panic("mismatch")
}
sMin, sMax, sErr := MinMax([]int{5})
if sErr != nil || sMin != 5 || sMax != 5 {
panic("mismatch on single item")
}
if _, _, err := MinMax(nil); err == nil {
panic("expected error on empty")
}
fmt.Printf("min: %d, max: %d\n", minVal, maxVal)
}
Go 在语言层面支持多返回值(Multiple Return Values),无需将结果打包成临时切片或结构体。
# Java
Javaimport java.util.List;
public class Main {
public record MinMax(int min, int max) {}
public static MinMax minMax(List<Integer> nums) {
if (nums == null || nums.isEmpty()) {
throw new IllegalArgumentException("nums cannot be empty");
}
int min = nums.get(0);
int max = nums.get(0);
for (int i = 1; i < nums.size(); i++) {
int x = nums.get(i);
if (x < min) min = x;
if (x > max) max = x;
}
return new MinMax(min, max);
}
public static void main(String[] args) {
MinMax res = minMax(List.of(3, 1, 9, 2));
assert res.min() == 1 && res.max() == 9;
MinMax single = minMax(List.of(5));
assert single.min() == 5 && single.max() == 5;
try {
minMax(List.of());
assert false : "should fail";
} catch (IllegalArgumentException expected) {
// 契约保证
}
System.out.println("min: " + res.min() + ", max: " + res.max());
}
}
Java 16 起引入不可变轻量数据载体 record,通过具名字段消除多返回值的顺序混淆。
# JavaScript
JavaScriptconst assert = {
equal: (a, b) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
deepEqual: (a, b) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
throws: (fn, errType) => {
try { fn(); throw new Error("expected throw"); } catch (e) {
if (e.message === "expected throw") throw e;
if (errType && !(e instanceof errType)) throw e;
}
}
};
function minMax(nums) {
if (!Array.isArray(nums) || nums.length === 0) {
throw new RangeError("nums cannot be empty");
}
let min = nums[0];
let max = nums[0];
for (let i = 1; i < nums.length; i++) {
const x = nums[i];
if (x < min) min = x;
if (x > max) max = x;
}
return { min, max };
}
const res = minMax([3, 1, 9, 2]);
assert.equal(res.min, 1);
assert.equal(res.max, 9);
const single = minMax([5]);
assert.equal(single.min, 5);
assert.equal(single.max, 5);
assert.throws(() => minMax([]), RangeError);
const { min, max } = res;
console.log(`min: ${min}, max: ${max}`);
JavaScript 返回具有语义属性名的字面量对象,调用方直接运用对象解构语法。
# TypeScript
TypeScriptconst assert = {
equal: (a: unknown, b: unknown) => { if (a !== b) throw new Error(`${a} !== ${b}`); },
deepEqual: (a: unknown, b: unknown) => { if (JSON.stringify(a) !== JSON.stringify(b)) throw new Error(`${JSON.stringify(a)} !== ${JSON.stringify(b)}`); },
throws: (fn: () => void, errType?: unknown) => {
try { fn(); throw new Error("expected throw"); } catch (e: unknown) {
if (e instanceof Error && e.message === "expected throw") throw e;
if (typeof errType === "function" && !(e instanceof errType)) throw e;
}
}
};
interface MinMax {
readonly min: number;
readonly max: number;
}
function minMax(nums: readonly number[]): MinMax {
if (nums.length === 0) {
throw new RangeError("nums cannot be empty");
}
let min = nums[0];
let max = nums[0];
for (let i = 1; i < nums.length; i++) {
const x = nums[i];
if (x < min) min = x;
if (x > max) max = x;
}
return { min, max };
}
const res = minMax([3, 1, 9, 2]);
assert.equal(res.min, 1);
assert.equal(res.max, 9);
assert.throws(() => minMax([]), RangeError);
const { min, max } = res;
console.log(`min: ${min}, max: ${max}`);
TypeScript 定义明确的接口返回类型,防止字段名称拼写错误。
# Kotlin
Kotlinfun minMax(nums: List<Int>): Pair<Int, Int> {
if (nums.isEmpty()) {
throw NoSuchElementException("nums cannot be empty")
}
var min = nums[0]
var max = nums[0]
for (i in 1 until nums.size) {
val x = nums[i]
if (x < min) min = x
if (x > max) max = x
}
return Pair(min, max)
}
fun main() {
val res = minMax(listOf(3, 1, 9, 2))
check(res.first == 1 && res.second == 9)
val (min, max) = res
println("min: $min, max: $max")
}
Kotlin 标准库包含 Pair,支持通过解构声明 val (min, max) = ... 直接解包其成员。
# Python
Pythonfrom typing import Sequence, Tuple
def min_max(nums: Sequence[int]) -> Tuple[int, int]:
if not nums:
raise ValueError("nums cannot be empty")
min_val = nums[0]
max_val = nums[0]
for x in nums[1:]:
if x < min_val:
min_val = x
if x > max_val:
max_val = x
return min_val, max_val
if __name__ == "__main__":
assert min_max([3, 1, 9, 2]) == (1, 9)
assert min_max([5]) == (5, 5)
try:
min_max([])
assert False, "should fail on empty"
except ValueError:
pass
min_val, max_val = min_max([3, 1, 9, 2])
print(f"min: {min_val}, max: {max_val}")
Python 逗号表达式自动打包为元组,调用方自然解包。
# C++
C++#include <iostream>
#include <vector>
#include <utility>
#include <optional>
#include <cassert>
std::optional<std::pair<int, int>> min_max(const std::vector<int>& nums) {
if (nums.empty()) {
return std::nullopt;
}
int min_val = nums[0];
int max_val = nums[0];
for (size_t i = 1; i < nums.size(); ++i) {
if (nums[i] < min_val) min_val = nums[i];
if (nums[i] > max_val) max_val = nums[i];
}
return std::make_pair(min_val, max_val);
}
int main() {
assert(min_max({3, 1, 9, 2}) == std::make_pair(1, 9));
assert(min_max({5}) == std::make_pair(5, 5));
assert(min_max({}) == std::nullopt);
if (auto res = min_max({3, 1, 9, 2})) {
auto [min_val, max_val] = *res; // C++17 结构化绑定
std::cout << "min: " << min_val << ", max: " << max_val << "\n";
}
}
C++17 引入结构化绑定(Structured Binding),可以直接将 std::pair 解包到局部变量中。
# 迁移误区与适用限制
- 匿名元组与位置依赖:若返回 3 个及以上同类型字段(如
(start, end, total, count)),位置型元组极易引发调用方解包顺序搞反的静默逻辑 Bug,此时应果断定义具名记录或对象。 - 伪造默认值:在空数组时返回
(0, 0)会导致调用方无法判断究竟是集合中真实存在元素0,还是因为无数据而产生的默认值。
# 运行与验证
语言版本基线见示例基线。 所有程序运行后控制台输出均为:
纯文本min: 1, max: 9
测试断言已完整覆盖多元素、单元素及空输入异常/空值保护。