Lambda表達式的前世今生

Lambda 表達式

早在 C# 1.0 時,C#中就引入了委托(delegate)類型的概念。通過使用這個類型,我們可以將函數作為參數進行傳遞。在某種意義上,委托可理解為一種托管的強類型的函數指針。

通常情況下,使用委托來傳遞函數需要一定的步驟:

  1. 定義一個委托,包含指定的參數類型和返回值類型。
  2. 在需要接收函數參數的方法中,使用該委托類型定義方法的參數簽名。
  3. 為指定的被傳遞的函數創建一個委托實例。

可能這聽起來有些復雜,不過本質上說確實是這樣。上面的第 3 步通常不是必須的,C# 編譯器能夠完成這個步驟,但步驟 1 和 2 仍然是必須的。

幸運的是,在 C# 2.0 中引入了泛型。現在我們能夠編寫泛型類、泛型方法和最重要的:泛型委托。盡管如此,直到 .NET 3.5,微軟才意識到實際上僅通過兩種泛型委托就可以滿足 99% 的需求:

  • Action :無輸入參數,無返回值
  • Action<T1, ..., T16> :支持1-16個輸入參數,無返回值
  • Func<T1, ..., T16, Tout> :支持1-16個輸入參數,有返回值

Action 委托返回 void 類型,Func 委托返回指定類型的值。通過使用這兩種委托,在絕大多數情況下,上述的步驟 1 可以省略了。但是步驟 2 仍然是必需的,但僅是需要使用 Action 和 Func。

那么,如果我只是想執行一些代碼該怎么辦?在 C# 2.0 中提供了一種方式,創建匿名函數。但可惜的是,這種語法并沒有流行起來。下面是一個簡單的匿名函數的示例:

      Func<double, double> square = delegate(double x){return x * x;};

為了改進這些語法,在 .NET 3.5 框架和 C# 3.0 中引入了Lambda 表達式。

首先我們先了解下 Lambda 表達式名字的由來。實際上這個名字來自微積分數學中的 λ,其涵義是聲明為了表達一個函數具體需要什么。更確切的說,它描述了一個數學邏輯系統,通過變量結合和替換來表達計算。所以,基本上我們有 0-n 個輸入參數和一個返回值。而在編程語言中,我們也提供了無返回值的 void 支持。

讓我們來看一些 Lambda 表達式的示例:

復制代碼
 1   // The compiler cannot resolve this, which makes the usage of var impossible! 
 2   // Therefore we need to specify the type.
 3   Action dummyLambda = () =>
 4   {
 5     Console.WriteLine("Hello World from a Lambda expression!");
 6   };
 7 
 8   // Can be used as with double y = square(25);
 9   Func<double, double> square = x => x * x;
10 
11   // Can be used as with double z = product(9, 5);
12   Func<double, double, double> product = (x, y) => x * y;
13 
14   // Can be used as with printProduct(9, 5);
15   Action<double, double> printProduct = (x, y) => { Console.WriteLine(x * y); };
16 
17   // Can be used as with 
18   // var sum = dotProduct(new double[] { 1, 2, 3 }, new double[] { 4, 5, 6 });
19   Func<double[], double[], double> dotProduct = (x, y) =>
20   {
21     var dim = Math.Min(x.Length, y.Length);
22     var sum = 0.0;
23     for (var i = 0; i != dim; i++)
24       sum += x[i] + y[i];
25     return sum;
26   };
27 
28   // Can be used as with var result = matrixVectorProductAsync(...);
29   Func<double[,], double[], Task<double[]>> matrixVectorProductAsync =
30     async (x, y) =>
31     {
32       var sum = 0.0;
33       /* do some stuff using await ... */
34       return sum;
35     };
復制代碼

從這些語句中我們可以直接地了解到:

  • 如果僅有一個入參,則可省略圓括號。
  • 如果僅有一行語句,并且在該語句中返回,則可省略大括號,并且也可以省略 return 關鍵字。
  • 通過使用 async 關鍵字,可以將 Lambda 表達式聲明為異步執行。
  • 大多數情況下,var 聲明可能無法使用,僅在一些特殊的情況下可以使用。

在使用 var 時,如果編譯器通過參數類型和返回值類型推斷無法得出委托類型,將會拋出 “Cannot assign lambda expression to an implicitly-typed local variable.” 的錯誤提示。來看下如下這些示例:

現在我們已經了解了大部分基礎知識,但一些 Lambda 表達式特別酷的部分還沒提及。

我們來看下這段代碼:

1   var a = 5;
2   Func<int, int> multiplyWith = x => x * a;
3 
4   var result1 = multiplyWith(10); // 50
5   a = 10;
6   var result2 = multiplyWith(10); // 100

可以看到,在 Lambda 表達式中可以使用外圍的變量,也就是閉包。

復制代碼
 1     static void DoSomeStuff()
 2     {
 3       var coeff = 10;
 4       Func<int, int> compute = x => coeff * x;
 5       Action modifier = () =>
 6       {
 7         coeff = 5;
 8       };
 9 
10       var result1 = DoMoreStuff(compute); // 50
11 
12       ModifyStuff(modifier);
13 
14       var result2 = DoMoreStuff(compute); // 25
15     }
16 
17     static int DoMoreStuff(Func<int, int> computer)
18     {
19       return computer(5);
20     }
21 
22     static void ModifyStuff(Action modifier)
23     {
24       modifier();
25     }
復制代碼

這里發生了什么呢?首先我們創建了一個局部變量和兩個 Lambda 表達式。第一個 Lambda 表達式展示了其可以在其他作用域中訪問該局部變量,實際上這已經展現了強大的能力了。這意味著我們可以保護一個變量,但仍然可以在其他方法中訪問它,而不用關心那個方法是定義在當前類或者其他類中。

第二個 Lambda 表達式展示了在 Lambda 表達式中能夠修改外圍變量的能力。這就意味著通過在函數間傳遞 Lambda 表達式,我們能夠在其他方法中修改其他作用域中的局部變量。因此,我認為閉包是一種特別強大的功能,但有時也可能引入一些非期望的結果。

復制代碼
 1       var buttons = new Button[10];
 2 
 3       for (var i = 0; i < buttons.Length; i++)
 4       {
 5         var button = new Button();
 6         button.Text = (i + 1) + ". Button - Click for Index!";
 7         button.OnClick += (s, e) => { Messagebox.Show(i.ToString()); };
 8         buttons[i] = button;
 9       }
10 
11       //What happens if we click ANY button?!
復制代碼

這個詭異的問題的結果是什么呢?是 Button 0 顯示 0, Button 1 顯示 1 嗎?答案是:所有的 Button 都顯示 10!

因為隨著 for 循環的遍歷,局部變量 i 的值已經被更改為 buttons 的長度 10。一個簡單的解決辦法類似于:

1       var button = new Button();
2       var index = i;
3       button.Text = (i + 1) + ". Button - Click for Index!";
4       button.OnClick += (s, e) => { Messagebox.Show(index.ToString()); };
5       buttons[i] = button;

通過定義變量 index 來拷貝變量 i 中的值。

注:如果你使用 Visual Studio 2012 以上的版本進行測試,因為使用的編譯器與 Visual Studio 2010 的不同,此處測試的結果可能不同。可參考:Visual C# Breaking Changes in Visual Studio 2012

表達式樹

在使用 Lambda 表達式時,一個重要的問題是目標方法是怎么知道如下這些信息的:

  1. 我們傳遞的變量的名字是什么?
  2. 我們使用的表達式體的結構是什么?
  3. 在表達式體內我們用了哪些類型?

現在,表達式樹幫我們解決了問題。它允許我們深究具體編譯器是如何生成的表達式。此外,我們也可以執行給定的函數,就像使用 Func 和 Action 委托一樣。其也允許我們在運行時解析 Lambda 表達式。

我們來看一個示例,描述如何使用 Expression 類型:

1       Expression<Func<MyModel, int>> expr = model => model.MyProperty;
2       var member = expr.Body as MemberExpression;
3       var propertyName = memberExpression.Member.Name; //only execute if member != null

上面是關于 Expression 用法的一個最簡單的示例。其中的原理非常直接:通過形成一個 Expression 類型的對象,編譯器會根據表達式樹的解析生成元數據信息。解析樹中包含了所有相關的信息,例如參數和方法體等。

方法體包含了整個解析樹。通過它我們可以訪問操作符、操作對象以及完整的語句,最重要的是能訪問返回值的名稱和類型。當然,返回變量的名稱可能為 null。盡管如此,大多數情況下我們仍然對表達式的內容很感興趣。對于開發人員的益處在于,我們不再會拼錯屬性的名稱,因為每個拼寫錯誤都會導致編譯錯誤。

如果程序員只是想知道調用屬性的名稱,有一個更簡單優雅的辦法。通過使用特殊的參數屬性 CallerMemberName 可以獲取到被調用方法或屬性的名稱。編譯器會自動記錄這些名稱。所以,如果我們僅是需要獲知這些名稱,而無需更多的類型信息,則我們可以參考如下的代碼寫法:

1 string WhatsMyName([CallerMemberName] string callingName = null)
2 {
3     return callingName;
4 }

Lambda 表達式的性能

有一個大問題是:Lambda 表達式到底有多快?當然,我們期待其應該與常規的函數一樣快,因為 Lambda 表達式也同樣是由編譯器生成的。在下一節中,我們會看到為 Lambda 表達式生成的 MSIL 與常規的函數并沒有太大的不同。

一個非常有趣的討論是關于在 Lambda 表達式中的閉包是否要比使用全局變量更快,而其中最有趣的地方就是是否當可用的變量都在本地作用域時是否會有性能影響。

讓我們來看一些代碼,用于衡量各種性能基準。通過這 4 種不同的基準測試,我們應該有足夠的證據來說明常規函數與 Lambda 表達式之間的不同了。

復制代碼
 1   class StandardBenchmark : Benchmark
 2   {
 3     static double[] A;
 4     static double[] B;
 5 
 6     public static void Test()
 7     {
 8       var me = new StandardBenchmark();
 9 
10       Init();
11 
12       for (var i = 0; i < 10; i++)
13       {
14         var lambda = LambdaBenchmark();
15         var normal = NormalBenchmark();
16         me.lambdaResults.Add(lambda);
17         me.normalResults.Add(normal);
18       }
19 
20       me.PrintTable();
21     }
22 
23     static void Init()
24     {
25       var r = new Random();
26       A = new double[LENGTH];
27       B = new double[LENGTH];
28 
29       for (var i = 0; i < LENGTH; i++)
30       {
31         A[i] = r.NextDouble();
32         B[i] = r.NextDouble();
33       }
34     }
35 
36     static long LambdaBenchmark()
37     {
38       Func<double> Perform = () =>
39       {
40         var sum = 0.0;
41 
42         for (var i = 0; i < LENGTH; i++)
43           sum += A[i] * B[i];
44 
45         return sum;
46       };
47       var iterations = new double[100];
48       var timing = new Stopwatch();
49       timing.Start();
50 
51       for (var j = 0; j < iterations.Length; j++)
52         iterations[j] = Perform();
53 
54       timing.Stop();
55       Console.WriteLine("Time for Lambda-Benchmark: \t {0}ms", 
56         timing.ElapsedMilliseconds);
57       return timing.ElapsedMilliseconds;
58     }
59 
60     static long NormalBenchmark()
61     {
62       var iterations = new double[100];
63       var timing = new Stopwatch();
64       timing.Start();
65 
66       for (var j = 0; j < iterations.Length; j++)
67         iterations[j] = NormalPerform();
68 
69       timing.Stop();
70       Console.WriteLine("Time for Normal-Benchmark: \t {0}ms", 
71         timing.ElapsedMilliseconds);
72       return timing.ElapsedMilliseconds;
73     }
74 
75     static double NormalPerform()
76     {
77       var sum = 0.0;
78 
79       for (var i = 0; i < LENGTH; i++)
80         sum += A[i] * B[i];
81 
82       return sum;
83     }
84   }
復制代碼

當然,利用 Lambda 表達式,我們可以把上面的代碼寫的更優雅一些,這么寫的原因是防止干擾最終的結果。所以我們僅提供了 3 個必要的方法,其中一個負責執行 Lambda 測試,一個負責常規函數測試,第三個方法則是在常規函數。而缺少的第四個方法就是我們的 Lambda 表達式,其已經在第一個方法中內嵌了。使用的計算方法并不重要,我們使用了隨機數,進而避免了編譯器的優化。最后,我們最感興趣的就是常規函數與 Lambda 表達式的不同。

在運行這些測試后,我們會發現,在通常情況下 Lambda 表達式不會表現的比常規函數更差。而其中的一個很奇怪的結果就是,Lambda 表達式實際上在某些情況下表現的要比常規方法還要好些。當然,如果是在使用閉包的條件下,結果就不一樣了。這個結果告訴我們,使用 Lambda 表達式無需再猶豫。但是我們仍然需要仔細的考慮當我們使用閉包時所丟失的性能。在這種情景下,我們通常會丟失一點性能,但或許仍然還能接受。關于性能丟失的原因將在下一節中揭開。

下面的表格中顯示了基準測試的結果:

  • 無入參無閉包比較

  • 含入參比較

  • 含閉包比較

  • 含入參含閉包比較

TestLambda [ms]Normal [ms]
045+-146+-1
144+-146+-2
249+-345+-2
348+-245+-2

注:測試結果根據機器硬件配置有所不同

下面的圖表中同樣展現了測試結果。我們可以看到,常規函數與 Lambda 表達式會有相同的限制。使用 Lambda 表達式并沒有顯著的性能損失。

MSIL揭秘Lambda表達式

使用著名的工具 LINQPad 我們可以查看 MSIL。

我們來看下第一個示例:

1 void Main()
2 {
3     DoSomethingLambda("some example");
4     DoSomethingNormal("some example");
5 }

Lambda 表達式:

1 Action<string> DoSomethingLambda = (s) =>
2 {
3     Console.WriteLine(s);// + local
4 };

相應的方法的代碼:

1 void DoSomethingNormal(string s)
2 {
3     Console.WriteLine(s);
4 }

兩段代碼的 MSIL 代碼:

復制代碼
 1 IL_0001:  ldarg.0     
 2 IL_0002:  ldfld       UserQuery.DoSomethingLambda
 3 IL_0007:  ldstr       "some example"
 4 IL_000C:  callvirt    System.Action<System.String>.Invoke
 5 IL_0011:  nop         
 6 IL_0012:  ldarg.0     
 7 IL_0013:  ldstr       "some example"
 8 IL_0018:  call        UserQuery.DoSomethingNormal
 9 
10 DoSomethingNormal:
11 IL_0000:  nop         
12 IL_0001:  ldarg.1     
13 IL_0002:  call        System.Console.WriteLine
14 IL_0007:  nop         
15 IL_0008:  ret         
16 
17 <.ctor>b__0:
18 IL_0000:  nop         
19 IL_0001:  ldarg.0     
20 IL_0002:  call        System.Console.WriteLine
21 IL_0007:  nop         
22 IL_0008:  ret  
復制代碼

此處最大的不同就是函數的命名和用法,而不是聲明方式,實際上聲明方式是相同的。編譯器會在當前類中創建一個新的方法,然后推斷該方法的用法。這沒什么特別的,只是使用 Lambda 表達式方便了許多。從 MSIL 的角度來看,我們做了相同的事,也就是在當前的對象上調用了一個方法。

我們可以將這些分析放到一張圖中,來展現編譯器所做的更改。在下面這張圖中我們可以看到編譯器將 Lambda 表達式移到了一個單獨的方法中。

在第二個示例中,我們將展現 Lambda 表達式真正神奇的地方。在這個例子中,我們使用了一個常規的方法來訪問全局變量,然后用一個 Lambda 表達式來捕獲局部變量。代碼如下:

復制代碼
 1 void Main()
 2 {
 3     int local = 5;
 4 
 5     Action<string> DoSomethingLambda = (s) => {
 6         Console.WriteLine(s + local);
 7     };
 8     
 9     global = local;
10     
11     DoSomethingLambda("Test 1");
12     DoSomethingNormal("Test 2");
13 }
14 
15 int global;
16 
17 void DoSomethingNormal(string s)
18 {
19     Console.WriteLine(s + global);
20 }
復制代碼

目前看來沒什么特殊的。關鍵的問題是:編譯器是如何處理 Lambda 表達式的?

復制代碼
 1 IL_0000:  newobj      UserQuery+<>c__DisplayClass1..ctor
 2 IL_0005:  stloc.1     // CS$<>8__locals2
 3 IL_0006:  nop         
 4 IL_0007:  ldloc.1     // CS$<>8__locals2
 5 IL_0008:  ldc.i4.5    
 6 IL_0009:  stfld       UserQuery+<>c__DisplayClass1.local
 7 IL_000E:  ldloc.1     // CS$<>8__locals2
 8 IL_000F:  ldftn       UserQuery+<>c__DisplayClass1.<Main>b__0
 9 IL_0015:  newobj      System.Action<System.String>..ctor
10 IL_001A:  stloc.0     // DoSomethingLambda
11 IL_001B:  ldarg.0     
12 IL_001C:  ldloc.1     // CS$<>8__locals2
13 IL_001D:  ldfld       UserQuery+<>c__DisplayClass1.local
14 IL_0022:  stfld       UserQuery.global
15 IL_0027:  ldloc.0     // DoSomethingLambda
16 IL_0028:  ldstr       "Test 1"
17 IL_002D:  callvirt    System.Action<System.String>.Invoke
18 IL_0032:  nop         
19 IL_0033:  ldarg.0     
20 IL_0034:  ldstr       "Test 2"
21 IL_0039:  call        UserQuery.DoSomethingNormal
22 IL_003E:  nop         
23 
24 DoSomethingNormal:
25 IL_0000:  nop         
26 IL_0001:  ldarg.1     
27 IL_0002:  ldarg.0     
28 IL_0003:  ldfld       UserQuery.global
29 IL_0008:  box         System.Int32
30 IL_000D:  call        System.String.Concat
31 IL_0012:  call        System.Console.WriteLine
32 IL_0017:  nop         
33 IL_0018:  ret         
34 
35 <>c__DisplayClass1.<Main>b__0:
36 IL_0000:  nop         
37 IL_0001:  ldarg.1     
38 IL_0002:  ldarg.0     
39 IL_0003:  ldfld       UserQuery+<>c__DisplayClass1.local
40 IL_0008:  box         System.Int32
41 IL_000D:  call        System.String.Concat
42 IL_0012:  call        System.Console.WriteLine
43 IL_0017:  nop         
44 IL_0018:  ret         
45 
46 <>c__DisplayClass1..ctor:
47 IL_0000:  ldarg.0     
48 IL_0001:  call        System.Object..ctor
49 IL_0006:  ret         
復制代碼

還是一樣,兩個函數從調用語句上看是相同的,還是應用了與之前相同的機制。也就是說,編譯器為該函數生成了一個名字,并把它替換到代碼中。而此處最大的區別在于,編譯器同時生成了一個類,而編譯器生成的函數就被放到了這個類中。那么,創建這個類的目的是什么呢?它使變量具有了全局作用域范圍,而此之前其已被用于捕獲變量。通過這種方式,Lambda 表達式有能力訪問局部作用域的變量(因為從 MSIL 的觀點來看,其僅是類實例中的一個全局變量而已)。

然后,通過這個新生成的類的實例,所有的變量都從這個實例分配和讀取。這解決了變量間存在引用的問題(會對類添加一個額外的引用 - 確實是這樣)。編譯器已經足夠的聰明,可以將那些被捕獲變量放到這個類中。所以,我們可能會期待使用 Lambda 表達式并不會存在性能問題。然而,這里我們必須提出一個警告,就是這種行為可能會引起內存泄漏,因為對象仍然被 Lambda 表達式引用著。只要這個函數還在,其作用范圍仍然有效(之前我們已經了解了這些,但現在我們知道了原因)。

像之前一樣,我們把這些分析放入一張圖中。從圖中我們可以看到,閉包并不是僅有的被移動的方法,被捕獲變量也被移動了。所有被移動的對象都會被放入一個編譯器生成的類中。最后,我們從一個未知的類實例化了一個對象。







本文轉自匠心十年博客園博客,原文鏈接:http://www.cnblogs.com/gaochundong/p/way_to_lambda.html,如需轉載請自行聯系原作者

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/391840.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/391840.shtml
英文地址,請注明出處:http://en.pswp.cn/news/391840.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

matplotlib柱狀圖、面積圖、直方圖、散點圖、極坐標圖、箱型圖

一、柱狀圖 1.通過obj.plot() 柱狀圖用bar表示&#xff0c;可通過obj.plot(kindbar)或者obj.plot.bar()生成&#xff1b;在柱狀圖中添加參數stackedTrue&#xff0c;會形成堆疊圖。 fig,axes plt.subplots(2,2,figsize(10,6)) s pd.Series(np.random.randint(0,10,15),index …

微信支付商業版 結算周期_了解商業周期

微信支付商業版 結算周期Economics is an inexact science, finance and investing even more so (some would call them art). But if there’s one thing in economics that you can consistently count on over the long run, it’s the tendency of things to mean revert …

leetcode 448. 找到所有數組中消失的數字

給定一個范圍在 1 ≤ a[i] ≤ n ( n 數組大小 ) 的 整型數組&#xff0c;數組中的元素一些出現了兩次&#xff0c;另一些只出現一次。 找到所有在 [1, n] 范圍之間沒有出現在數組中的數字。 您能在不使用額外空間且時間復雜度為O(n)的情況下完成這個任務嗎? 你可以假定返回…

前端初學者開發學習視頻_初學者學習前端開發的實用指南

前端初學者開發學習視頻by Nikita Rudenko通過尼基塔魯登科(Nikita Rudenko) 初學者學習前端開發的實用指南 (A practical guide to learning front end development for beginners) I started my coding journey in spring 2018, a bit less than one year ago. I earned som…

weblogic啟動失敗案例(root啟動引起的權限問題)

weblogic的一個domain啟動失敗&#xff0c;在日志中有如下信息提示&#xff1a; **************************************************** To start WebLogic Server, use a username and ** password assigned to an admin-level user. For ** server administration, us…

HTTP請求示例

HTTP請求格式當瀏覽器向Web服務器發出請求時&#xff0c;它向服務器傳遞了一個數據塊&#xff0c;也就是請求信息&#xff0c;HTTP請求信息由3部分組成&#xff1a;l 請求方法URI協議/版本l 請求頭(Request Header)l 請求正文下面是一個HTTP請求的例子&#xff1a;GET/sa…

Bootstrap——可拖動模態框(Model)

還是上一個小項目&#xff0c;o(╥﹏╥)o&#xff0c;要實現點擊一個div或者button或者一個東西然后可以彈出一個浮在最上面的彈框。網上找了找&#xff0c;發現Bootstrap的Model彈出框可以實現該功能&#xff0c;因此學習了一下&#xff0c;實現了基本彈框功能&#xff08;可拖…

mfcc中的fft操作_簡化音頻數據:FFT,STFT和MFCC

mfcc中的fft操作What we should know about sound. Sound is produced when there’s an object that vibrates and those vibrations determine the oscillation of air molecules which creates an alternation of air pressure and this high pressure alternated with low …

leetcode 765. 情侶牽手(并查集)

N 對情侶坐在連續排列的 2N 個座位上&#xff0c;想要牽到對方的手。 計算最少交換座位的次數&#xff0c;以便每對情侶可以并肩坐在一起。 一次交換可選擇任意兩人&#xff0c;讓他們站起來交換座位。 人和座位用 0 到 2N-1 的整數表示&#xff0c;情侶們按順序編號&#xff…

ariel字體_播客第58集:軟件開發人員和freeCodeCamp超級巨星Ariel Leslie

ariel字體On this weeks episode of the freeCodeCamp.org podcast, Abbey interviews Ariel Leslie, a software developer and avid contributor to the freeCodeCamp community.在本周的freeCodeCamp.org播客節目中&#xff0c;Abbey采訪了Ariel Leslie&#xff0c;他是free…

PHP繪制3D圖形

PEAR提供了Image_3D Package來創建3D圖像。圖像或光線在3D空間中按照X、Y 、Z 坐標定位。生成的圖像將呈現在2D空間中&#xff0c;可以存儲為 PNG、SVG 格式&#xff0c;或輸出到Shell。通過Image_3D可以很方便生成一些簡單的3D對象&#xff0c;例如立方體、錐體、球體、文本和…

清除日志的sql

SET NOCOUNT ONDECLARE LogicalFileName sysname,MaxMinutes INT,NewSize INTUSE cms -- 要操作的數據庫名SELECT LogicalFileName cms_log, -- 日志文件名MaxMinutes 10, -- Limit on time allowed to wrap log.NewSize 100 -- 你想設定的日志文件的大小(M)-- Setup / init…

r語言怎么以第二列繪制線圖_用衛星圖像繪制世界海岸線圖-第二部分

r語言怎么以第二列繪制線圖Part I of this blog series is here.本博客系列的第一部分 在這里 。 At the UKHO we are interested in the oceans, the seabed and the coastline — not to mention everything in and on them! In our previous blog, we (the UKHO Data Scien…

javascript創建類_如何在10分鐘內使用JavaScript創建費用管理器

javascript創建類by Per Harald Borgen通過Per Harald Borgen 如何在10分鐘內使用JavaScript創建費用管理器 (How to create an expense organizer with JavaScript in 10 minutes) 讓我們使用ES6和Dropbox API來防止收據變得混亂。 (Let’s use ES6 and the Dropbox API to k…

豆瓣API

Api V2 索引 圖書Api V2 電影Api V2 音樂Api V2 同城Api V2 廣播Api V2 用戶Api V2 日記Api V2 相冊Api V2 線上活動Api V2 論壇Api V2 回復Api V2 我去Api V2 https://developers.douban.com/wiki/?titleapi_v2 搜索圖書 GET https://api.douban.com/v2/book/search參數意義…

leetcode 485. 最大連續1的個數

給定一個二進制數組&#xff0c; 計算其中最大連續1的個數。 示例 1: 輸入: [1,1,0,1,1,1] 輸出: 3 解釋: 開頭的兩位和最后的三位都是連續1&#xff0c;所以最大連續1的個數是 3. 解題思路 遇到0時&#xff0c;將連續1的長度歸零。遇到1時&#xff0c;累加進長度 代碼 c…

HDU Today

經過錦囊相助&#xff0c;海東集團終于度過了危機&#xff0c;從此&#xff0c;HDU的發展就一直順風順水&#xff0c;到了2050年&#xff0c;集團已經相當規模了&#xff0c;據說進入了錢江肉絲經濟開發區500強。這時候&#xff0c;XHD夫婦也退居了二線&#xff0c;并在風景秀美…

JSP基礎--動作標簽

JSP基礎--動作標簽 JSP動作標簽 1 JSP動作標簽概述 動作標簽的作用是用來簡化Java腳本的&#xff01; JSP動作標簽是JavaWeb內置的動作標簽&#xff0c;它們是已經定義好的動作標簽&#xff0c;我們可以拿來直接使用。 如果JSP動作標簽不夠用時&#xff0c;還可以使用自定義標…

整數存儲怎么轉化為浮點數_非整數值如何存儲在浮點數中(以及為什么要浮點數)...

整數存儲怎么轉化為浮點數by Shukant Pal通過Shukant Pal 非整數值如何存儲在浮點數中(以及為什么要浮點數) (How non-integer values are stored in a float (and why it floats)) Did you ever think how computers work on floating-point numbers? I mean — where does …

rcp rapido_Rapido使用數據改善乘車調度

rcp rapidoGiven our last blog post of the series, which can be found here :鑒于我們在該系列中的最后一篇博客文章&#xff0c;可以在這里找到&#xff1a; We thought it would be helpful to explain how we implemented all of the above into an on-ground experimen…