我对函数式编程相当新,并且在列表处理任务方面存在一些问题。我有一系列记录如下:
type TestRec = {
Id : string
Amount : int }
现在我要删除列表中彼此构建一对的所有项目。例如,如果有两个Amount
7
-7
和Amount = 7
项,则应从列表中删除项。如果第三个元素包含let removeDoubles items =
items
|> Seq.groupBy (fun i -> Math.Abs(i.Amount))
|> Seq.map snd
|> Seq.filter (fun i -> Seq.length i = 1)
,则应保留在列表中。
我希望你们能理解我想要做的事情。这是我到目前为止提出的(但它还没有正常工作):
Math.Abs
修改
确定两个元素是否相互匹配的函数可能比上面描述的更复杂(Amount
)。我认为这是{{1}}值的一个很好的例子,但它可以是任何谓词函数。
编辑2: 为了澄清更多,我想对可能的相关问题给出更真实的描述。您可以想象一下发票的计算,其中列表包含所有发票头寸。现在,您要删除所有具有相同“商品编号”,“货币”且价格评估为零的发票头寸。
也许这个例子有助于解释我的问题。我只是觉得解决这个问题可能有一个更“功能性的方法”,就是在列表上运行两个循环并删除像我在命令式语言中那样的元素。
答案 0 :(得分:1)
为了抽象出对立的想法,我定义了两个函数。一个用于关系相等(相关),另一个用于定义取消(相反)。
该功能首先将相关对象分组在一起,然后将它们分成相对的数组。然后根据所需的取消数量对这些得到的阵列进行切片。最后,所有内容都汇总在一起。
type TestRec = {
Id : string;
Amount : int;
}
let removeDoubles items related opposite =
items
|> Seq.groupBy related
|> Seq.map (fun (key, values) ->
let t, f = values |> Seq.toArray |> Array.partition opposite
if t.Length > f.Length then
t.[.. t.Length - f.Length - 1]
else
f.[.. f.Length - t.Length - 1]
)
|> Seq.concat
let items = [
{Id="first";Amount=7};
{Id="seconds";Amount=7};
{Id="we";Amount=4};
{Id="negative";Amount= -7}
]
let test = removeDoubles
items
(fun x -> abs x.Amount)
(fun x -> x.Amount > 0)
printf "%A" test
System.Console.ReadLine() |> ignore
输出
seq [{Id = "first";
Amount = 7;}; {Id = "we";
Amount = 4;}]
答案 1 :(得分:0)
(根据您的评论更新)
如果你想删除连续的否定对,你可以这样做:
let removePairs f items =
let rec loop acc = function
| a :: b :: t when f a b -> loop acc t
| h :: t -> loop (h::acc) t
| [] -> List.rev acc
loop [] (List.ofSeq items)
items |> removePairs (fun {Amount=amtA} {Amount=amtB} -> amtA + amtB = 0)
答案 2 :(得分:0)
另一种选择,可能不如其他提案那么有用,但应该有效:
type R = {
Id : int
Amount : int
}
let mkR id amount = {Id = id; Amount = amount}
open System.Collections.Generic
let removePairs s : seq<R> =
// stores mapping: key -> corresponding nodes in result list
let seen = Dictionary<_, LinkedList<LinkedListNode<R>>>()
// accumulates result
let list = LinkedList<R>()
// check if paired element was already met
// if yes - remove its first appearance from the result list
let tryEliminate ({Amount = a} as el) =
// paired element will have different sign
let key = -a
match seen.TryGetValue key with
| true, existing ->
list.Remove(existing.First.Value) // Remove(LinkedListNode) works in O(1)
existing.RemoveFirst()
if existing.Count = 0 then seen.Remove key |> ignore
true
| _ ->
false
let append ({Amount = a} as el) =
let newNode = list.AddLast(el)
let l =
match seen.TryGetValue a with
| true, existing -> existing
| false, _ ->
let nodes = LinkedList()
seen.Add(a, nodes)
nodes
l.AddLast(newNode) |> ignore
for el in s do
if not (tryEliminate el) then append el
list :> _
let check source expected =
let result =
source
|> List.mapi (fun i x -> {Id = i; Amount = x})
|> removePairs
|> List.ofSeq
if (result <> expected) then failwithf "Expected: %A, actual %A" expected result
check [1;1;-1;2] [mkR 1 1; mkR 3 2]
check [1;1;-1;-1] []
check [7;7;4] [mkR 0 7; mkR 1 7; mkR 2 4]