In the case of functional programming, it is important to think of a highly flexible and dynamic lisp,haskell such as the ancient functional language, and to say that ruby,javascript,f# is also a popular language for functional programming. However, since. NET supports lambda expressions, C #, as an instruction-oriented programming language, is no less in functional programming. In the process of writing code in C #, we intentionally and unintentionally use higher-order functions, combinatorial functions, pure function caches, and so on, even the idea of the expression tree comes from functional programming. So then we summarize the usual functional programming scenarios, which will help us to apply these techniques flexibly in the process of programming, expand our design ideas and improve the quality of our code.
First, higher order function
Higher-order functions are popular: functions are used as parameters in a function, and such functions are called higher-order functions. According to this definition, the large number of LINQ expressions used in. NET, Where,select,selectmany,first, and so on are high-level functions, so when do we use this design when we write our own code?
Example: Design a function for calculating the cost of a property, Var fee=square*price, and the area (square) is calculated differently depending on the nature of the property. For residential, commercial housing and so on need to multiply the different coefficients, according to this demand we try to design the following function:
Residential area of civil housing:
Public func<int,int,decimal> Squareforcivil () { return (width,hight) =>width*hight;}
Commercial House size:
public func<int, int, decimal> squareforbusiness () { return (width, hight) = width * HIGHT*1.2M;}
These functions all have a common signature: func<int,int,decimal> So we can use this function signature to design a function that calculates the cost of a property:
Public decimal propertyfee (decimal price,int width,int hight, func<int, int, decimal> square) { return price* Square (width, hight);}
Is it easy to write a test to see
[test]public void Should_calculate_propertyfee_for_two_area () { //arrange var calculator = new Propertyfeecalculator (); Act var feeforbusiness= calculator. Propertyfee (2m,2, 2, Calculator. Squareforbusiness ()); var feeforcivil = Calculator. Propertyfee (1m, 2, 2, Calculator. Squareforcivil ()); Assert feeforbusiness.should (). Be (9.6m); Feeforcivil.should (). Be (4m);}
Second, the lazy evaluation
C # uses a rigorous evaluation strategy in its execution, so-called rigorous evaluation refers to the evaluation of parameters before they are passed to the function. Is this explanation still a little unclear? Let's take a look at a scenario where a task needs to be performed, requires that the current memory usage is less than 80%, and the result of the previous step is <100 to meet this condition in order to perform the task.
We can quickly write C # code that meets this requirement:
Public double memoryutilization () { //calculates current memory utilization var pcinfo = new ComputerInfo (); var usedmem = pcinfo.totalphysicalmemory-pcinfo.availablephysicalmemory; Return (double) (Usedmem/convert.todecimal (pcinfo.totalphysicalmemory)); } public int bigcalculatationforfirststep () { ///first-step operation System.Threading.Thread.Sleep ( Timespan.fromseconds (2)); Console.WriteLine ("Big Calulation"); Firststepexecuted = true; return 10; } public void NextStep (double memoryutilization,int firststepdistance) {//Next operation if (memoryutilization< 0.8&&firststepdistance<100) { Console.WriteLine ("Next Step");} }
The calculation of the memory usage and the first step (function Bigcalculatationforfirststep) is required to execute the NEXTSTEP, as shown in the code, the first operation is a time-consuming operation, but due to the strict evaluation strategy of C #, for the statement if ( MEMORYUTILIZATION<0.8&&FIRSTSTEPDISTANCE<100), even if the memory usage is already greater than 80%, the first operation has to be performed, obviously, if memory usage is greater than 80%, Value Firststepdistance is no longer important, it can be done without calculation.
So lazy evaluation means that an expression or part of an expression is evaluated only when it really needs its results. We try to rewrite this requirement with higher-order functions:
public void Nextstepwithorderfunction (func<double> memoryutilization,func<int> firstStep) { if ( Memoryutilization () < 0.8 && Firststep () < [+] { Console.WriteLine ("Next Step");} }
The code is simple, which is to replace the function value with a function expression if (memoryutilization () < 0.8: This is not enough, the function behind it will not execute. Microsoft has added the Lazy<t> class to the. net4.0 version, and you can use this mechanism in scenarios where this is required.
Three, function curry (Curry)
Curry is also referred to as local apply. Definition: A technique that transforms a function that accepts multiple parameters into a function that takes a single parameter (the first parameter of the original function) and returns a new function that takes the remaining parameters and returns the result, PS: Why does the official explanation go so round?
It's hard to see how this is going to happen, so let's start with the curry principle:
Write a function that adds two numbers:
public func<int, int, int> addtwonumber () { return (x, y) = = x + y;}
OK, how do I use this function?
var result= _curringreasoning.addtwonumber ();
1+2=3, the call is simple. To upgrade the requirements, we need a function that requires the input of a parameter (number) to calculate the result of the parameter (number) of the + + input. Estimated someone to say, this needs the above code can be fully implemented Ah, the first parameter you passed 10 is not finished, OK, if you think so, I also helpless. Others may say, and then write an overload, as long as a parameter can be, the reality is not allowed, we call other people to provide the API, cannot add overloading. It is not a common scenario to see local application scenarios, so the best design is to fit the right technology in the right scenario, and we'll look at the implementation of local implementations:
Public Func<int, Func<int, int>> addtwonumbercurrying () { func<int, func<int, int>> addcurrying = x = y + = x + y; return addcurrying;}
The expression x = y = x + y Gets the function signature for Func<int, Func<int, int>> This function signature is very clear, receive an int type parameter, get a func<int,int> Type of function. At this point if we call again:
Actvar Curringresult = curringreasoning.addtwonumbercurrying (), var result = Curringresult (2); Assertresult.should (). Be (12);
This sentence: var curringresult = curringreasoning.addtwonumbercurrying () (10); The generated function is to receive only one parameter (number), and the 10+number function can be computed.
For the same reason, the three-number function:
Public func<int,int,int,int> Addthreenumber () { return (x, y, Z) = + x + y + z;}
Locally applied version:
Public func<int,func<int,func<int,int>>> addthreenumbercurrying () { func<int, Func<int , Func<int, int>>> addcurring = x = y = z + = x + y + z; return addcurring;}
Call Procedure:
[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);}
When the function parameters are more, manual local application is more and more difficult to write, we can use the extension method to automatically apply:
public static func<t1, Func<t2, tresult>> curry<t1, T2, tresult> (this func<t1, T2, tresult> Func) { return x = y = = func (x, y);} public static func<t1, Func<t2, Func<t3, tresult>>> curry<t1, T2, T3, tresult> (this func<t1, T 2, t3,tresult> func) { return x = y = Z=>func (x, y,z);}
Similarly, the,action<> signature function can also be applied automatically.
With these extension methods, it's easier to use local application.
[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);}
Well, in this case, StackOverflow has several articles about the scenes and definitions used by currying, which you can continue to understand.
Functional programming also has some important ideas, such as: pure function of the cache, the pure function refers to the function of the call is not affected by the outside world, the same parameter call the resulting value is always the same. The tail recursion, the list, the code is the data (expression tree in. net), some applications, the combination of functions, some of which I am still learning, some are still thinking about their best use of the scene, so no longer summed up, if one day to grasp the idea will be added.
Iv. Design Cases
Finally I want to design a scene, the high-order functions, lambda expressions, generic methods together, I design such an example is because there are many frameworks, open source projects have a similar way of writing, it is because of a combination of technology and ideas, there is very expressive and very elegant code.
Requirements: Design a word finder, which can find out whether some fields of an incoming model contain a word, because different model has different fields, so the lookup needs to be configured, and you can take advantage of the smart hints of vs.
This feature actually has two methods:
Private ReadOnly list<func<string, bool>> _conditions; Public wordfinder<tmodel> find<tproperty> (func<tmodel,tproperty> expression) { func< String, bool> searchcondition = Word = = Expression (_model). ToString (). Split ('). Contains (word); _conditions. ADD (searchcondition); return this;} public bool Execute (string wordList) { return _conditions. Any (x=>x (wordList));}
Use:
[test]public void Should_find_a_word () { //arrange var article = new article () { title = "This is a title" , content = "This is the content", Comment = "This was 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);
The case itself is not practical, but you can see that it is a comprehensive application of various technologies to design a very semantic API, if the function parameter is changed to expression<func<tmodel,tproperty>> type, We can also read information such as the specific property name.