惰性求值 php,详细介绍C#函数式编程的示例代码
public double MemoryUtilization()
{
//計算目前內存使用率
var pcInfo = new ComputerInfo();
var usedMem = pcInfo.TotalPhysicalMemory - pcInfo.AvailablePhysicalMemory;
return (double)(usedMem / Convert.ToDecimal(pcInfo.TotalPhysicalMemory));
}
public int BigCalculatationForFirstStep()
{
//第一步運算
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(2));
Console.WriteLine("big calulation");
FirstStepExecuted = true;
return 10;
}
public void NextStep(double memoryUtilization,int firstStepDistance)
{
//下一步運算
if(memoryUtilization<0.8&&firststepdistance<100) {="" console.writeline("next="" step");="" }="" }
在執行NextStep的時候需要傳入內存使用率和第一步(函數BigCalculatationForFirstStep)的計算結果,如代碼所示,第一步操作是一個很費時的運算,但是由于C#的嚴格求值策略,對于語句if(memoryUtilization<0.8&&firststepdistance<100)來講,即使內存使用率已經大于80%了,第一步操作還得執行,很顯然,如果內存使用率大于80%,值firststepdistance已經不重要了,完全可以不用計算。
所以惰性求值是指:表達式或者表達式的一部分只有當真正需要它們的結果時才會對它們進行求值。我們嘗試用高階函數來重寫這個需求:public void NextStepWithOrderFunction(Func memoryUtilization,Func firstStep)
{
if (memoryUtilization() < 0.8 && firstStep() < 100)
{
Console.WriteLine("Next step");
}
}
代碼很簡單,就是用一個函數表達式來代替函數值,如果if (memoryUtilization() < 0.8..這句不滿足,后面的函數也不會執行。微軟在.net4.0版本中加入了Lazy類,大家可以在有這種需求的場景下使用這個機制。
三、函數柯里化(Curry)
柯里化也稱作局部套用。定義:是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回接受余下的參數且返回結果的新函數的技術,ps:為什么官方解釋這么繞口?
看到這樣的定義估計大家也很難明白這是這么一回事,所以我們從curry的原理講起:
寫一個兩個數相加的函數:
public Func AddTwoNumber()
{
return (x, y) => x + y;
}
ok, 如何使用這個函數?
var result= _curringReasoning.AddTwoNumber()(1,2);
1+2=3,調用很簡單。需求升級,我們需要一個函數,這個函數要求輸入一個參數(number),算出10+輸入的參數(number)的結果。估計有人要說了,這需求上面的代碼完全可以實現啊,第一個參數你傳入10不就完了么,ok,如果你是這樣想的,我也是無可奈何。還有人可能說了,再寫一個重載,只要一個參數即可,實際情況是不容許,我們在調用別人提供的api,無法添加重載??梢钥吹骄植刻子玫氖褂脠鼍安皇且环N很普遍的場景,所以在合適的場景配合合適的技術才是最好的設計,我們來看局部套用的實現:
public Func AddTwoNumberCurrying()
{
Func addCurrying = x => y => x + y;
return addCurrying;
}
表達式x => y => x + y得到的函數簽名為Func,這個函數簽名非常清楚,接收一個int類型的參數,得到一個Func類型的函數。此時如果我們再調用:
//Act
var curringResult = curringReasoning.AddTwoNumberCurrying()(10);
var result = curringResult(2);
//Assert
result.Should().Be(12);
這句話:var curringResult = curringReasoning.AddTwoNumberCurrying()(10); 生成的函數就是只接收一個參數(number),且可以計算出10+number的函數。
同樣的道理,三個數相加的函數:
public Func AddThreeNumber()
{
return (x, y, z) => x + y + z;
}
局部套用版本:
public Func> AddThreeNumberCurrying()
{
Func> addCurring = x => y => z => x + y + z;
return addCurring;
}
調用過程:
[Test]
public void Three_number_add_test()
{
//Arrange
var curringReasoning = new CurryingReasoning();
//Act
var result1 = curringReasoning.AddThreeNumber()(1, 2, 3);
var curringResult = curringReasoning.AddThreeNumberCurrying()(1);
var curringResult2 = curringResult(2);
var result2 = curringResult2(3);
//Assert
result1.Should().Be(6);
result2.Should().Be(6);
}
當函數參數多了之后,手動局部套用越來越不容易寫,我們可以利用擴展方法自動局部套用:
public static Func Curry(this Func func)
{
return x => y => func(x, y);
}
public static Func> Curry(this Func func)
{
return x => y => z=>func(x, y,z);
}
同樣的道理,Action簽名的函數也可以自動套用
有了這些擴展方法,使用局部套用的時候就更加easy了
[Test]
public void Should_auto_curry_two_number_add_function()
{
//Arrange
var add = _curringReasoning.AddTwoNumber();
var addCurrying = add.Curry();
//Act
var result = addCurrying(1)(2);
//Assert
result.Should().Be(3);
}
好了,局部套用就說到這里,stackoverflow有幾篇關于currying使用的場景和定義的文章,大家可以繼續了解。
函數式編程還有一些重要的思想,例如:純函數的緩存,所為純函數是指函數的調用不受外界的影響,相同的參數調用得到的值始終是相同的。尾遞歸,單子,代碼即數據(.net中的表達式樹),部分應用,組合函數,這些思想有的我也仍然在學習中,有的還在思考其最佳使用場景,所以不再總結,如果哪天領會了其思想會補充。
四、設計案例
最后我還是想設計一個場景,把高階函數,lambda表達式,泛型方法結合在一起,我之所以設計這樣的例子是因為現在很多的框架,開源的項目都有類似的寫法,也正是因為各種技術和思想結合在一起,才有了極富有表達力并且非常優雅的代碼。
需求:設計一個單詞查找器,該查找器可以查找某個傳入的model的某些字段是否包含某個單詞,由于不同的model具有不同的字段,所以該查找需要配置,并且可以充分利用vs的智能提示。
這個功能其實就兩個方法:
private readonly List _conditions;
public WordFinder Find(Func expression)
{
Func searchCondition = word => expression(_model).ToString().Split(' ').Contains(word);
_conditions.Add(searchCondition);
return this;
}
public bool Execute(string wordList)
{
return _conditions.Any(x=>x(wordList));
}
使用:
[Test]
public void Should_find_a_word()
{
//Arrange
var article = new Article()
{
Title = "this is a title",
Content = "this is content",
Comment = "this is comment",
Author = "this is author"
};
//Act
var result = Finder.For(article)
.Find(x => x.Title)
.Find(x => x.Content)
.Find(x => x.Comment)
.Find(x => x.Author)
.Execute( "content");
//Assert
result.Should().Be(true);
}
該案例本身不具有實用性,但是大家可以看到,正是各種技術的綜合應用才設計出極具語義的api, 如果函數參數改為Expression 類型,我們還可以讀取到具體的屬性名稱等信息。
總結
以上是生活随笔為你收集整理的惰性求值 php,详细介绍C#函数式编程的示例代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 上海欢乐谷成人两次入园票可以两个人用吗
- 下一篇: oracle 分区使用情况,Oracle