List 如何使用F#在没有第一项和最后一项的情况下获取子列表?

List 如何使用F#在没有第一项和最后一项的情况下获取子列表?,list,f#,sublist,List,F#,Sublist,我已对整数值列表进行了排序: let ls = [1..4] 没有第一个和最后一个元素,如何获得子列表?(以最佳方式) 预期结果是[2;3] 这就是我到目前为止所做的,是的,这是有效的,但我认为这不是最好的方法 [1..4] |> List.tail |> List.rev |> List.tail |> List.sort 您不需要标准的库函数来实现这一点,您只是需要一种有效的方法。定义一个包含中间结果的累加器的递归函数将是一个可行的解决方案,即使列表在终止时必须反

我已对整数值列表进行了排序:

let ls = [1..4]
没有第一个和最后一个元素,如何获得子列表?(以最佳方式)

预期结果是
[2;3]

这就是我到目前为止所做的,是的,这是有效的,但我认为这不是最好的方法

[1..4] |> List.tail |> List.rev |> List.tail |> List.sort

您不需要标准的库函数来实现这一点,您只是需要一种有效的方法。定义一个包含中间结果的累加器的递归函数将是一个可行的解决方案,即使列表在终止时必须反转

我提供了一个自定义的有区别的联合来跟踪状态,这是按照
选项类型
建模的,并带有一个额外的案例

type 'a State = Zero | One | Other of 'a

let skipFirstAndLast xss =
    let rec aux acc = function
    | _,            []    -> List.rev acc
    | Zero,         x::xs -> aux acc (One, xs)
    | One,          x::xs -> aux acc (Other x, xs)
    | (Other prev), x::xs -> aux (prev :: acc) (Other x, xs)
    aux [] (Zero, xss)

[1..4] |> skipFirstAndLast // val it : int list = [2; 3]

这是一种方式:

let rec trim ls acc =
    match ls, acc with
    | [],      _   -> acc
    | h::[],   acc -> List.rev acc
    | h::n::t, []  -> trim t [n]  
    | h::t,    acc -> trim t (h::acc)

let reslt = trim ls []

一个有点长的答案回应了你那天真的限定词:“以最理想的方式”

在什么方面是最优的

  • 表演?(最有可能)
  • 性能,但也包括GC性能
  • 内存使用情况
  • x86
  • x64
  • 等等

    所以我决定对问题的某些方面进行测量

    我在不同的上下文中测量了这个线程中的不同答案(还添加了一个非惯用的版本)

    这里是我用来测量的程序

    open System
    open System.Diagnostics
    open System.IO
    
    module so29100251 =
        // Daystate solution (OP)
        module Daystate =
            // Applied minor fixes to it
            let trim = function
                | [] | [_] | [_;_] -> []
                | ls -> ls |> List.tail |> List.rev |> List.tail |> List.rev
    
        // kaefer solution
        module kaefer =
            type 'a State = Zero | One | Other of 'a
    
            let skipFirstAndLast xss =
                let rec aux acc = function
                | _,            []    -> List.rev acc
                | Zero,         x::xs -> aux acc (One, xs)
                | One,          x::xs -> aux acc (Other x, xs)
                | (Other prev), x::xs -> aux (prev :: acc) (Other x, xs)
                aux [] (Zero, xss)
    
        // Petr solution
        module Petr =
            let rec trimImpl ls acc =
                match ls, acc with
                | [],      _   -> acc
                | h::[],   acc -> List.rev acc
                | h::n::t, []  -> trimImpl t [n]
                | h::t,    acc -> trimImpl t (h::acc)
    
            let trim ls = trimImpl ls []
    
        // NonIdiomatic solution
        module NonIdiomatic =
            let trim (hint : int) (ls : 'T list) =
                // trims last of rest
    
                // Can't ask for ls.Length as that is O(n)
                let ra = ResizeArray<_> (hint)
    
                // Can't use for x in list do as it relies on .GetEnumerator ()
                let mutable c = ls
                while not c.IsEmpty do
                    ra.Add c.Head
                    c <- c.Tail
    
                let count = ra.Count
    
                let mutable result = []
                for i in (count - 2)..(-1)..1 do
                    result <- ra.[i]::result
                result
    
    open so29100251
    
    type Time = MilliSeconds of int64
    
    type TestKind<'T> =
         | Functional               of 'T
         | MeasurePerformance       of int*int
    
    [<EntryPoint>]
    let main argv =
        let factor  = 10000000
    //    let maxHint = Int32.MaxValue
        let maxHint = 100
    
        let time (action : unit -> 'T) : 'T*Time =
            let sw = Stopwatch ()
    
            sw.Start ()
    
            let r = action ()
    
            sw.Stop ()
    
            r, MilliSeconds sw.ElapsedMilliseconds
    
        let adapt fn hint ls = fn ls
    
        let trimmers =
            [|
                "Daystate"      , adapt Daystate.trim
                "kaefer"        , adapt kaefer.skipFirstAndLast
                "Petr"          , adapt Petr.trim
                "NonIdiomatic"  , NonIdiomatic.trim
            |]
    
    
    #if DEBUG
        let functionalTestCases =
            [|
                Functional []               , "empty"       , []
                Functional []               , "singleton"   , [1]
                Functional []               , "duoton"      , [1;2]
                Functional [2]              , "triplet"     , [1;2;3]
                Functional [2;3]            , "quartet"     , [1;2;3;4]
            |]
    
        let performanceMeasurements = [||]
    #else
        let functionalTestCases = [||]
    
        let performanceMeasurements =
            [|
                "small"   , 10
                "big"     , 1000
                "bigger"  , 100000
    //            "huge"    , 10000000
            |] |> Array.map (fun (name, size) -> MeasurePerformance (size, (factor / size))  , name       , [for x in 1..size -> x])
    #endif
    
        let testCases =
            [|
                functionalTestCases
                performanceMeasurements
            |] |> Array.concat
    
    
        use tsv = File.CreateText ("result.tsv")
    
        tsv.WriteLine (sprintf "TRIMMER\tTESTCASE\tSIZE\tHINT\tRUNS\tMEMORY_BEFORE\tMEMORY_AFTER\tGC_TIME\tRUN_TIME")
    
        for trimName, trim in trimmers do
            for testKind, testCaseName, testCase in testCases do
                match testKind with
                | Functional expected ->
                    let actual = trim 0 testCase
                    if actual = expected then
                        printfn "SUCCESS: Functional test of %s trim on testcase %s successful" trimName testCaseName
                    else
                        printfn "FAILURE: Functional test of %s trim on testcase %s failed" trimName testCaseName
                | MeasurePerformance (size,testRuns) ->
    
                    let hint    = min size maxHint
    
                    let before  = GC.GetTotalMemory(true)
    
                    printfn "MEASURE: Running performance measurement on %s trim using testcase %s..." trimName testCaseName
    
                    let timeMe () =
                        for x in 1..testRuns do
                            ignore <| trim hint testCase
                    let _, MilliSeconds ms = time timeMe
    
                    let after   = GC.GetTotalMemory(false)
    
                    let timeGC () =
                        ignore <| GC.GetTotalMemory(true)
                    let _, MilliSeconds msGC = time timeMe
    
                    printfn "...%d ms (%d runs), %d (before) %d (after) %d ms (GC)" ms testRuns before after msGC
    
                    tsv.WriteLine (sprintf "%s\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d" trimName testCaseName size hint testRuns before after msGC ms)
    
        0
    
    开放系统
    开放系统诊断
    开放系统
    模块so29100251=
    //日状态解决方案(OP)
    模块日状态=
    //对它进行了一些小的修正
    让修剪=功能
    | [] | [_] | [_;_] -> []
    |ls->ls |>List.tail |>List.rev |>List.tail |>List.rev
    //卡弗溶液
    卡弗模块=
    键入“a状态=零|一个|另一个”
    让skipFirstAndLast xss=
    让rec aux acc=功能
    |_u,[]->List.rev acc
    |零,x::xs->aux acc(一,xs)
    |一个,x::xs->aux acc(其他x,xs)
    |(其他前置),x::xs->aux(前置::acc)(其他x,xs)
    辅助[](零,xss)
    //Petr溶液
    模块Petr=
    让rec TRIMINPL ls acc=
    将ls、acc与
    |[],->acc
    |h::[],acc->List.rev acc
    |h::n::t,[]->trimpl t[n]
    |h::t,acc->Trimpl t(h::acc)
    让trim ls=trimmpl ls[]
    //非惯用解
    非惯用模块=
    让修剪(提示:int)(ls:'T列表)=
    //修剪剩下的部分
    //不能要求ls.长度,因为这是O(n)
    设ra=ResizeArray(提示)
    //无法用于列表中的x,因为它依赖于.GetEnumerator()
    设可变c=ls
    而不是c.我是空的
    ra.添加c.头
    c Array.map(fun(name,size)->MeasurePerformance(size,(factor/size)),name,[对于1中的x..size->x])
    #恩迪夫
    让测试用例=
    [|
    功能测试用例
    性能测量
    |]|>Array.concat
    使用tsv=File.CreateText(“result.tsv”)
    tsv.WriteLine(sprintf“TRIMMER\tTESTCASE\tSIZE\tHINT\tRUNS\tMEMORY\u之前\tMEMORY\u之后\tGC\u时间\tRUN\u时间”)
    对于trimName,修剪器中的修剪操作
    对于testKind、testCaseName、testCase中的testCase
    将testKind与
    |功能性预期->
    让实际值=修剪0测试用例
    如果实际=预期,则
    printfn“成功:在测试用例%s上对%s trim进行功能测试成功”trimName testCaseName
    其他的
    printfn“失败:在测试用例%s上对%s trim的功能测试失败”trimName testCaseName
    |测量性能(大小、测试运行)->
    let hint=最小大小maxHint
    let before=GC.GetTotalMemory(true)
    printfn“度量:使用testcase%s对%s trim运行性能度量…”trimName testCaseName
    让我()=
    对于1中的x..testRuns
    
    忽略你似乎没有一个列表,但你的问题仍然不清楚。“最佳方式”是什么,列表的典型长度是多少等等。这里是一个很好的起点:列表不应该包含超过100个元素。如果您知道列表已经排序,那么倒序可能比最后一步排序更快:
    [1..4]|>List.tail |>List.rev |>List.tail |>List.rev
    。有了排序,就有了所有的条件逻辑和比较--
    sort
    是O(n logn),而不仅仅是O(n),就像
    rev
    一样。这是正确的。感谢您指出这一点。它适用于
    [1..4]
    但对于
    [1..5]
    结果列表是按降序排列的。哇!我希望我能给出超过+1的答案谢谢@Petr的反馈。哇,这是我得到的最棒的答案!我明天再谈。非常感谢你!我怀疑x86更好,因为对象更小——对象引用只有原来的一半大。因此,列表中的更多节点存储在同一页面上,页面错误也更少。