Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/250.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# For循环输入列表_F#_Control Flow - Fatal编程技术网

F# For循环输入列表

F# For循环输入列表,f#,control-flow,F#,Control Flow,人们经常使用 for i in [0 .. 10] do something 但如果创建一个列表,然后进行迭代,那么在我看来,使用它会更有意义 for i = 0 to 10 do something 没有创建不必要的列表,但具有相同的行为。 我错过什么了吗?(我想是这样的)你是对的,在[0..10]中为I编写“do something”会生成一个列表,它确实有很大的开销。虽然您也可以省略方括号,但在这种情况下,它只构建了一个惰性序列(而且,事实证明编译器甚至优化了这种情况)。我通常更喜欢在

人们经常使用

for i in [0 .. 10] do something
但如果创建一个列表,然后进行迭代,那么在我看来,使用它会更有意义

for i = 0 to 10 do something
没有创建不必要的列表,但具有相同的行为。
我错过什么了吗?(我想是这样的)

你是对的,在[0..10]中为I编写
“do something”
会生成一个列表,它确实有很大的开销。虽然您也可以省略方括号,但在这种情况下,它只构建了一个惰性序列(而且,事实证明编译器甚至优化了这种情况)。我通常更喜欢在0中编写
。。100 do
,因为它看起来与在序列上迭代的代码相同

使用F#interactive的
#time
功能进行简单分析:

for i in [ 0 .. 10000000 ] do // 3194ms (yikes!)
  last <- i

for i in 0 .. 10000000 do     // 3ms
  last <- i

for i = 0 to 10000000 do      // 3ms
  last <- i

for i in seq { 0 .. 10000000 } do // 709ms (smaller yikes!)
  last <- i
[0..10000000]do//3194ms(yikes!)中的i的


最后前一种形式需要语言中的特殊构造(对于var from…to…by),这是古代编程语言遵循的方式:

  • Fortran中的“do”循环
  • 对于var:=expr到expr(以帕斯卡为单位)
  • 等等
后一种形式(表示某物中的var)更为通用。它适用于普通列表,但也适用于生成器(如python)等。在运行列表之前,可能不需要构建完整列表。这允许在可能无限的列表上写入循环

无论如何,一个好的编译器/解释器应该认识到相当频繁的特例[expr1..expr2],并避免中间列表的计算和存储。

给出了一种不同的答案,但希望对某些人来说有趣

在这种情况下,F#编译器无法应用快速for循环优化,这是正确的。好消息是,F#编译器是开源的,我们可以改进它的行为

这是我的免费赠品:

快速for循环优化发生在。这是一个相当原始的时刻,对我们来说是一个很好的机会来提高

// Detect the compiled or optimized form of a 'for <elemVar> in <startExpr> .. <finishExpr>  do <bodyExpr>' expression over integers
// Detect the compiled or optimized form of a 'for <elemVar> in <startExpr> .. <step> .. <finishExpr>  do <bodyExpr>' expression over integers when step is positive
let (|CompiledInt32ForEachExprWithKnownStep|_|) g expr = 
    match expr with 
    | Let (_enumerableVar, RangeInt32Step g (startExpr, step, finishExpr), _, 
           Let (_enumeratorVar, _getEnumExpr, spBind,
              TryFinally (WhileLoopForCompiledForEachExpr (_guardExpr, Let (elemVar,_currentExpr,_,bodyExpr), m), _cleanupExpr))) -> 

        let spForLoop = match spBind with SequencePointAtBinding(spStart) -> SequencePointAtForLoop(spStart) |  _ -> NoSequencePointAtForLoop 

        Some(spForLoop,elemVar,startExpr,step,finishExpr,bodyExpr,m)
    | _ -> 
        None

let DetectFastIntegerForLoops g expr = 
    match expr with 
    | CompiledInt32ForEachExprWithKnownStep g (spForLoop,elemVar,startExpr,step,finishExpr,bodyExpr,m) 
         // fast for loops only allow steps 1 and -1 steps at the moment
         when step = 1 || step = -1 -> 
            mkFastForLoop  g (spForLoop,m,elemVar,startExpr,(step = 1),finishExpr,bodyExpr)
    | _ -> expr
您如何确定这是您需要匹配的模式?我经常采用的方法是使用正确的属性编写一个简单的F#程序,并在编译过程中放置一个断点来检查表达式。由此,我创建了要匹配的模式:

让我们把这两种模式放在一起:

let (|ExtractInt32Range|_|) g expr =
  match expr with
  | RangeInt32Step g range -> Some range
  | SeqRangeInt32Step g range -> Some range
  | _ -> None
compiledint22foreachxprwithknownstep
更新为使用
ExtractInt32Range
over
RangeInt32Step

完整的解决方案如下:

let (|SeqRangeInt32Step|_|) g expr =
    match expr with
    // detect '[n .. m]'
    | Expr.App(Expr.Val(toList,_,_),_,[TType_var _],
                [Expr.App(Expr.Val(seq,_,_),_,[TType_var _],
                          [Expr.Op(TOp.Coerce, [TType_app (seqT, [TType_var _]); TType_var _],
                                    [RangeInt32Step g (startExpr, step, finishExpr)], _)],_)],_)
        when
            valRefEq g toList (ValRefForIntrinsic g.seq_to_list_info) &&
            valRefEq g seq g.seq_vref &&
            tyconRefEq g seqT g.seq_tcr ->
            Some(startExpr, step, finishExpr)

    | _ -> None

let (|ExtractInt32Range|_|) g expr =
  match expr with
  | RangeInt32Step g range -> Some range
  | SeqRangeInt32Step g range -> Some range
  | _ -> None

// Detect the compiled or optimized form of a 'for <elemVar> in <startExpr> .. <finishExpr>  do <bodyExpr>' expression over integers
// Detect the compiled or optimized form of a 'for <elemVar> in <startExpr> .. <step> .. <finishExpr>  do <bodyExpr>' expression over integers when step is positive
let (|CompiledInt32ForEachExprWithKnownStep|_|) g expr = 
    match expr with 
    | Let (_enumerableVar, ExtractInt32Range g (startExpr, step, finishExpr), _,
           Let (_enumeratorVar, _getEnumExpr, spBind,
              TryFinally (WhileLoopForCompiledForEachExpr (_guardExpr, Let (elemVar,_currentExpr,_,bodyExpr), m), _cleanupExpr))) -> 

        let spForLoop = match spBind with SequencePointAtBinding(spStart) -> SequencePointAtForLoop(spStart) |  _ -> NoSequencePointAtForLoop 

        Some(spForLoop,elemVar,startExpr,step,finishExpr,bodyExpr,m)
    | _ -> 
        None
let(| SeqRangeInt32Step | | |)g expr=
匹配表达式
//检测“[n..m]”
|Expr.App(Expr.Val(toList,u,u),u,[t类型u变量u],
[Expr.App(Expr.Val(seq,u,u),[t类型u变量],
[Expr.Op(TOp.胁迫,[t类型应用程序(seqT,[t类型变量]);t类型变量],
[RangeInt32步骤g(开始、步骤、完成expr)],[uu1],[u2],[u2],[u2]
什么时候
valRefEq g toList(ValrefFor内部g.seq至列表信息)&&
valRefEq g seq g.seq_vref&&
tyconRefEq g序列g.seq_tcr->
一些(startExpr、step、finishExpr)
|无
let(| ExtractInt32Range | |)g expr=
匹配表达式
|RangeInt32步骤g范围->某些范围
|SeqRangeInt32步骤g范围->某些范围
|无
//检测“for in.”的编译或优化形式。。整数上的do'表达式
//检测“for in…”的编译或优化形式。。阶跃为正时整数上的do表达式
let(| compiledint22foreachexprwithknownstep | | |)g expr=
匹配表达式
|Let(_enumerableVar,ExtractInt32Range g(startExpr,step,finishExpr)),
Let(_enumeratorVar,_getEnumExpr,spBind,
尝试最终(当LoopforCompiledForEachExpr(\u-guardExpr,Let(elemVar,\u-currentExpr,\u,bodyExpr,m),\u-cleanupExpr))时->
让spForLoop=将spBind与SequencePointAtBinding(spStart)->SequencePointAtForLoop(spStart)| |->NoSequencePointTorLoop匹配
一些(spForLoop、elemVar、startExpr、step、finishExpr、bodyExpr、m)
| _ -> 
没有一个
使用简单的测试程序

let print v =
    printfn "%A" v

[<EntryPoint>]
let main argv =
    for x in [0..10] do
        print x

    0
让我们打印v=
打印fn“%A”v
[]
让主argv=
对于[0..10]中的x,请执行以下操作
打印x
0
在进行优化之前,相应的C#代码应该是这样的(IL代码更易于检查,但如果不习惯,则可能有点难以理解):

//测试
[入口点]
公共静态int main(字符串[]argv)
{
FSharpList FSharpList=SeqModule.ToList(Operators.CreateSequence(Operators.OperatorIntrinsics.RangeInt32(0,1,10));
IEnumerator枚举器=((IEnumerable)fSharpList).GetEnumerator();
尝试
{
while(枚举数.MoveNext())
{
测试。打印(枚举器。当前);
}
}
最后
{
IDisposable disposable=作为IDisposable的枚举器;
if(一次性!=null)
{
一次性的,一次性的;
}
}
返回0;
}
F#创建一个列表,然后使用枚举器对其进行迭代。难怪它比经典的for循环要慢

应用优化后,我们得到以下代码:

// Test
[EntryPoint]
public static int main(string[] argv)
{
    for (int i = 0; i < 11; i++)
    {
        Test.print<int>(i);
    }
    return 0;
}
//测试
[入口点]
公共静态int main(字符串[]argv)
{
对于(int i=0;i<11;i++)
{
测试打印(一);
}
返回0;
}
这是一个显著的进步

所以,偷取这个密码,发布一个公关,享受荣耀。当然,您需要添加单元测试和发出的IL代码测试,这可能有点棘手,无法找到合适的级别,请检查此项以获取灵感

PS.可能还应该支持
[|0..10}]
seq{0..10}


另外,
对于0L..10L中的v执行打印v
以及
对于0..2..10中的v执行打印v
在F#中也是无效的。

只是出于好奇,我在启用llvm优化器的mono上尝试了这一点。这没用;我得到了大致相同的结果。我不确定第一个结果
// Test
[EntryPoint]
public static int main(string[] argv)
{
    FSharpList<int> fSharpList = SeqModule.ToList<int>(Operators.CreateSequence<int>(Operators.OperatorIntrinsics.RangeInt32(0, 1, 10)));
    IEnumerator<int> enumerator = ((IEnumerable<int>)fSharpList).GetEnumerator();
    try
    {
        while (enumerator.MoveNext())
        {
            Test.print<int>(enumerator.Current);
        }
    }
    finally
    {
        IDisposable disposable = enumerator as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }
    return 0;
}
// Test
[EntryPoint]
public static int main(string[] argv)
{
    for (int i = 0; i < 11; i++)
    {
        Test.print<int>(i);
    }
    return 0;
}