我想创建一个产生两个输出的函数。 请考虑以下示例:
我构建了两个函数,给定一个整数列表,返回偶数位置的元素列表和奇数位置的元素。
let rec alternate1 lst =
match lst with
[] -> []
| [x] -> []
| x::y::xs -> y::(alternate1 xs)
let rec alternate2 lst =
match lst with
[] -> []
| [x] -> [x]
| x::y::xs -> x::(alternate2 xs)
这里一切都很好。现在,问题是:我想创建一个单个函数alternate
,它返回带有签名alternate: int list-> (int list * int list)
的两个列表。
let rec alternate lst =
match lst with
[] -> []
| [x] -> []
| [x::y] -> [y]
(*My attempts:*)
| x::y::xs -> ((y::alternate xs), (x::alternate xs))
| x::y::xs -> [(y::alternate xs); (x::alternate xs)]
| x::y::xs -> ((y::alternate xs) && (x::alternate xs))
到目前为止,还没有解决方案。我很确定问题甚至是愚蠢的,但我的reference并没有帮助我解决问题。
答案 0 :(得分:6)
由于你以递归方式调用alternate
,递归调用也会返回两个输出,所以当然你不能将该元组视为列表 - 就像在{{ 1}}。
你必须首先拆开元组,分别处理这些部分,并在返回之前将它们重新组合成一个元组:
y::alternate xs
然后,您的基本案例也应该返回两个输出 - 否则您的函数返回类型不清楚:
let nextXs, nextYs = alternate xs
x::nextXs, y::nextYs
(另请注意,您的匹配案例| [] -> [], []
| [x] -> [x], []
| [x; y] -> [x], [y]
实际上匹配列表列表,其中只包含一个列表,其中第一个元素将命名为[x::y]
,列表的尾部将为名为x
。为了匹配两个元素的列表,请使用y
或[x; y]
)
将它们组合在一起:
x::y::[]
另外:从技术上讲,不需要let rec alternate lst =
match lst with
| [] -> [], []
| [x] -> [x], []
| [x; y] -> [x], [y]
| x::y::rest ->
let nextXs, nextYs = alternate rest
x::nextXs, y::nextYs
基本情况,因为它会被最后一种情况所涵盖。
答案 1 :(得分:3)
Fyodor's answer完成(像往常一样)
所以我只想添加一个尾递归版本的代码(以及一些减少)给那些想知道如何使其尾递归的人(使用continuation-passing style)
let alternate xs =
let aux cont even odd (evens, odds) = cont (even :: evens, odd :: odds)
let rec loop cont = function
| [] | [_] as xs -> cont (xs, [])
| even :: odd :: xs -> loop (aux cont even odd) xs
loop id xs
或者,可以为每个方列表使用2个延续,但在这里我认为它不是那么有用,因为每次都会操纵 sides ,但无论如何
let alternate xs =
let aux cont x xs = cont (x :: xs)
let rec loop evenCont oddCont = function
| [] | [_] as xs -> evenCont xs, oddCont []
| even :: odd :: xs -> loop (aux evenCont even) (aux oddCont odd) xs
loop id id xs