基于多元素值条件过滤F#列表的函数实现方法
F#函数式实现:提取列表中两个连续0及之前的所有元素
给定列表:
let x = [10; 9; 8; 7; 7; 6; 6; 5; 5; 4; 4; 4; 3; 3; 2; 2; 1; 0; 0; 0; -1; -2; -3]
需要返回结果:
[10; 9; 8; 7; 7; 6; 6; 5; 5; 4; 4; 4; 3; 3; 2; 2; 1; 0; 0]
方法1:递归实现(最纯粹的函数式方式)
递归是函数式编程的核心,通过维护遍历状态来终止逻辑:
let takeUntilTwoConsecutiveZeros list = let rec loop acc remaining = match remaining with | a :: b :: rest when a = 0 && b = 0 -> List.rev (b :: a :: acc) | [] -> List.rev acc | head :: rest -> loop (head :: acc) rest loop [] list
思路:
- 内部递归函数
loop维护两个状态:已收集的元素(acc,头插方式存储,最终需反转)和剩余未遍历列表 - 模式匹配检查剩余列表的前两个元素,若均为0则将其加入收集列表后反转返回
- 若列表为空,直接返回反转后的收集元素
- 否则将当前头元素加入收集列表,继续递归遍历剩余部分
调用示例:
takeUntilTwoConsecutiveZeros x // 得到预期结果
方法2:标准库函数组合(简洁实现)
利用F#标准库的windowed和takeWhile函数,通过组合操作完成需求:
let takeUntilTwoConsecutiveZeros list = list |> List.windowed 2 |> List.takeWhile (fun pair -> pair.[0] <> 0 || pair.[1] <> 0) |> List.map List.head |> fun prefix -> prefix @ [0; 0]
思路:
List.windowed 2将原列表转换为相邻元素对的列表,例如[[10;9]; [9;8]; ...; [1;0]; [0;0]; ...]List.takeWhile保留所有不满足“两个元素都是0”的元素对,得到到[1;0]为止的所有元素对List.map List.head提取每个元素对的第一个元素,得到[10;9;...;1]- 最后手动拼接两个0,得到完整目标列表
方法3:scan状态跟踪实现
用scan遍历列表并维护状态,标记是否找到连续两个0:
let takeUntilTwoConsecutiveZeros list = list |> List.scan (fun (found, acc) elem -> if found then (found, acc) else match acc with | [] -> (false, [elem]) | last :: _ when last = 0 && elem = 0 -> (true, elem :: last :: acc) | _ -> (false, elem :: acc)) (false, []) |> List.find (fun (found, _) -> found) |> snd |> List.rev
思路:
scan初始状态为(false, []),表示未找到目标、收集元素为空- 遍历元素时,若已找到目标则保持状态;否则检查收集列表的最后一个元素是否为0且当前元素也是0,若是则标记为已找到并加入元素
- 找到第一个标记为
found = true的状态,取出收集列表并反转得到结果
内容的提问来源于stack exchange,提问作者Eugene
相关产品推荐
相关产品推荐

