SML 中的一个对列表进行切片的函数?
Posted
技术标签:
【中文标题】SML 中的一个对列表进行切片的函数?【英文标题】:A function in SML that slices a list? 【发布时间】:2021-12-31 00:18:57 【问题描述】:我正在尝试用 SML 编写一个对列表进行切片的函数。
Slice x y ls
例如。 Slice 1 3 [0,1,2,3,4]
和输出 [1,2,3]
这是我必须开始的事情
fun slice(aList, start, stop) = nil;
fun slice(nil, x, y) = []
| slice(ls, x, y) =
在 SML 中编写一个切片函数,其功能类似于 Python 列表切片运算符。
例如slice ([11, 22, 3, 14, 5, 6], 1, 4)
返回列表[22, 3, 14]
返回索引之间的列表片段,以包含开始,并以独占结束。假设列表的第一个元素位于索引0
。
谢谢!感谢您的帮助!
【问题讨论】:
所以第一个参数是首先要查找的值,然后将所有内容都放入,直到找到第二个值?到目前为止,您尝试过什么来解决这个问题? 这是我目前所拥有的,我真的不知道如何处理它 fun slice(x, y, nil) = 0 |切片(x,y,ls)= 问题:你能写一个函数来返回一个忽略所有之前给定值的列表吗?你可以做同样的事情,但在给定值之后忽略所有内容吗?每个复杂的问题都只是多个更简单的问题。x
和 y
是索引还是列表元素?如果x
是索引,那么y
是索引还是切片的长度?
0
对于生成列表的函数来说不是一个好的基本情况。
【参考方案1】:
基本解决方案
你怎么知道你目前在哪个索引上?
您必须将该信息传递给函数。让我们将其称为“当前”cur
。
对空列表进行切片会返回一个空列表。对非空列表进行切片应该查看列表中的第一个元素和当前索引,如果当前索引落入start
和stop
提供的范围内,则通过将其附加到结果中将其添加到输出中对列表的其余部分运行相同的功能。
fun slice([], start, stop, cur) = []
| slice(x::xs, start, stop, cur) =
if cur >= start andalso cur <= stop then
x :: slice(xs, start, stop, cur + 1)
else
slice(xs, start, stop, cur + 1)
考虑当我们评估slice([1, 3, 8, 2, 9, 10, 47], 2, 4, 0)
时会发生什么:
slice([1, 3, 8, 2, 9, 10, 47], 2, 4, 0)
slice([3, 8, 2, 9, 10, 47], 2, 4, 1)
slice([8, 2, 9, 10, 47], 2, 4, 2)
8 :: slice([2, 9, 10, 47], 2, 4, 3)
8 :: 2 :: slice([9, 10, 47], 2, 4, 4)
8 :: 2 :: 9 :: slice([10, 47], 2, 4, 5)
8 :: 2 :: 9 :: slice([47], 2, 4, 6)
8 :: 2 :: 9 :: slice([47], 2, 4, 6)
8 :: 2 :: 9 :: slice([], 2, 4, 7)
8 :: 2 :: 9 :: []
[8, 2, 9]
隐藏丑陋
这很好用,但是看到传递给函数的0
的当前索引很难看。让我们使用本地辅助函数来隐藏它。
fun slice(lst, start, stop) =
let
fun aux([], start, stop, cur) = []
| aux(x::xs, start, stop, cur) =
if cur >= start andalso cur <= stop then
x :: aux(xs, start, stop, cur + 1)
else
aux(xs, start, stop, cur + 1)
in
aux(lst, start, stop, 0)
end
尾调用优化
这很好,但我们可以让它尾递归吗?我们可以,但是我们需要一个累加器,我们将它从一个递归调用传递到下一个构建结果列表。此外,在aux
的基本情况下,我们不需要命名start
、stop
或cur
,因为它们与结果无关,因此我们可以使用_
。
当我们累加结果时,它会反向建立,所以当我们返回累加器时,我们将它反转。
fun slice(lst, start, stop) =
let
fun aux([], _, _, _, acc) = List.rev(acc)
| aux(x::xs, start, stop, cur, acc) =
if cur >= start andalso cur <= stop then
aux(xs, start, stop, cur + 1, x::acc)
else
aux(xs, start, stop, cur + 1, acc)
in
aux(lst, start, stop, 0, [])
end
递归的短路
现在,这将遍历整个列表。当cur
大于stop
时,我们可以停止并返回累加器。这将节省一些精力。
fun slice(lst, start, stop) =
let
fun aux([], _, _, _, acc) = List.rev(acc)
| aux(x::xs, start, stop, cur, acc) =
if cur > stop then
List.rev(acc)
else if cur >= start andalso cur <= stop then
aux(xs, start, stop, cur + 1, x::acc)
else
aux(xs, start, stop, cur + 1, acc)
in
aux(lst, start, stop, 0, [])
end
使用折叠
这是我们可以应用折叠的另一个地方。 List.foldl
提供了一个工具,我们可以使用它来消除我们自己函数中的递归迭代。
使用foldl
,我们可以将索引和累加器从一个迭代传递到下一个迭代。每次我们对该信息应用一个函数以将其转换为下一次迭代。只有当索引在start
和stop
之间时,我们才会将它添加到累加器中。
完成后,我们丢弃索引信息并留下一个我们可以反转的结果列表。
fun slice'(lst, start, stop) =
let
val (_, lst') = foldl
(fn (v, (idx, acc)) =>
if idx >= start andalso idx <= stop then
(idx + 1, v::acc)
else
(idx + 1, acc))
(0, [])
lst
in
List.rev(lst')
end;
我们真正在做什么?
在这种情况下,“切片”列表是过滤基于每个元素的索引是否在范围内的列表。因此,让我们定义一些新函数。一、inRange
:
fun inRange(start, stop, x) =
x >= start andalso x <= stop
然后我们将定义一个函数indexedFilter
,它接受一个列表和一个谓词函数。谓词函数将接受两个参数:一个元素和一个索引。如果谓词返回 true,我们将保留该元素。
定义将使用foldl
。
fun indexedFilter(lst, pred) =
let
val (lst', _) =
List.foldl
(fn (x, (acc, idx)) =>
if pred(x, idx) then (x :: acc, idx + 1)
else (acc, idx + 1))
([], 0)
lst
in
List.rev lst'
end
现在slice
可以定义为:
fun slice(lst, start, stop) =
indexedFilter(lst, fn (_, idx) => inRange(start, stop, idx))
【讨论】:
如果不介意的话,能不能把第一组代码(基本解决方案)逐行解释一下,让我更明白? 这真的进入了非常基本的领域。您使用什么资源或资源来学习? 我正在尝试提供帮助。似乎您对语法和语义知之甚少。 SO 旨在帮助解决具体的、重点突出的问题。如果我们知道您接触过什么,我们可以更好地帮助您。以上是关于SML 中的一个对列表进行切片的函数?的主要内容,如果未能解决你的问题,请参考以下文章