Use C # For Functional Programming

Source: Internet
Author: User

Use C # For Functional Programming
When talking about functional programming, you must think of LISP, an ancient functional language like Haskell, which is highly flexible and dynamic in syntax. ruby, javascript, F # is also a popular language for functional programming. However, since. net supports lambda expressions, C #, as a directive programming language, is not inferior in functional programming. In the process of writing code using c #, we use high-order functions, composite functions, pure function caching, and other ideas intentionally or unintentionally, idea, such as the Expression Tree, also comes from functional programming ideas. So next we will make a summary of common functional programming scenarios, which will help us to flexibly apply these technologies in the program design process, expand our design ideas and improve the code quality. 1. High-order functions in general: A function uses a function as a parameter. Such a function is called a high-order function. According to this definition ,. the frequently used LINQ expressions, Where, Select, SelectMany, and First in net all belong to high-order functions. When will we use this design when writing our own code? For example, to design a function for calculating the property fee, var charge = square * price. The area (square) varies according to the property nature, and the calculation method is different. Civil residential buildings and commercial residential buildings must be multiplied by different coefficients. Based on such requirements, we try to design the following function: civil residential area: public Func <int, int, decimal> SquareForCivil () {return (width, hight) => width * hight;} commercial residential area: public Func <int, int, decimal> SquareForBusiness () {return (width, hight) => width * hight * 1.2 m;} all these functions share the same signature: Func <int, int, decimal>, so we can use this function signature to design the function for fee calculation: public decimal propertytypes (decimal price, int width, int hight, Func <int, int, decimal> square) {retur N price * square (width, hight);} Is it easy? Write a Test to see [Test] public void Should_calculate_propertyFee_for_two_area () {// Arrange var calculator = new PropertyFeeCalculator (); // Act var feeForBusiness = calculator. propertyator (2 m, 2, 2, calculator. squareForBusiness (); var feeForCivil = calculator. propertyator (1 m, 2, 2, calculator. squareForCivil (); // Assert feeForBusiness. shocould (). must (9.6 m); feeForCivil. shoul D (). be (4 m);} 2. Evaluate inertia C # use a strict evaluate policy in the execution process. The so-called strict evaluate refers to the value of a parameter before it is passed to the function. Is this explanation still not clear enough? Let's take a look at this scenario: There is a task that needs to be executed. The current memory usage must be less than 80%, and the result calculated in the previous step is <100. This condition is met before the task can be executed. We can quickly write out the C # code that meets this requirement: public double MemoryUtilization () {// calculate the current memory usage var pcInfo = new ComputerInfo (); var usef8 = pcInfo. totalPhysicalMemory-pcInfo. availablePhysicalMemory; return (double) (usemoodle/Convert. toDecimal (pcInfo. totalPhysicalMemory);} public int BigCalculatationForFirstStep () {// The first operation is System. threading. thread. sleep (TimeSpan. fromSeconds (2); Console. writeLine ("big calulation"); Fi RstStepExecuted = true; return 10;} public void NextStep (double memoryUtilization, int firstStepDistance) {// next operation if (memoryUtilization <0.8 & firstStepDistance <100) {Console. writeLine ("Next step") ;}} when executing NextStep, You need to input the memory usage and the calculation result of step 1 (function BigCalculatationForFirstStep), as shown in the code, the first step is a very time-consuming operation, but due to the strict evaluate policy of C #, for the statement if (memoryUtilization <0.8 & firstStepDistance <100, even if the memory usage is greater than 80%, the first step must be performed. Obviously, if the memory usage is greater than 80%, The value firstStepDistance is no longer important and does not need to be calculated. Therefore, the inertia evaluation refers to the Evaluation of expressions or expressions only when they are actually needed. We try to use higher-order functions to rewrite this requirement: public void NextStepWithOrderFunction (Func <double> memoryUtilization, Func <int> firstStep) {if (memoryUtilization () <0.8 & firstStep () & lt; 100) {Console. writeLine ("Next step") ;}} the code is very simple, that is, a function expression is used to replace the function value. if (memoryUtilization () <0. 8 .. this statement is not true, and subsequent functions will not be executed. Microsoft added the Lazy <T> class in. net4.0. You can use this mechanism in such scenarios. 3. Curry is also called local application. Definition: converts a function that accepts multiple parameters into a function that accepts a single parameter (the first parameter of the original function, and return the technology of the new function that accepts the remaining parameters and returns results. ps: Why does the official explanation say this? It is hard for everyone to understand this definition, so from the perspective of curry's 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, we need a function. This function requires entering a parameter (number) to calculate the result of 10 + input parameters (number. It may be said that the above Code can be fully implemented. The first parameter you passed in 10 is not enough. OK. If you think so, I can't help it. Another person can say, write another overload, as long as one parameter. The actual situation is not allowed. We cannot add a overload when calling the api provided by others. We can see that the use scenario of local application is not a very common scenario, so the best design is to use the appropriate technology in the appropriate scenario. Let's look at the implementation of local application: public Func <int, Func <int, int> AddTwoNumberCurrying () {Func <int, Func <int, int> addCurrying = x => y => x + y; return addCurrying;} expression x => y => the function signature obtained by x + y is Func <int, Func <int, int>. The function signature is clear, receives an int type parameter and obtains a function of the Func <int, int> type. Now, if we call: // Act var curringResult = curringReasoning. addTwoNumberCurrying () (10); var result = curringResult (2); // Assert result. shocould (). be (12); var curringResult = curringReasoning. addTwoNumberCurrying () (10); The generated function is a function that receives only one parameter (number) and can calculate 10 + numbers. In the same way, the three functions are added: public Func <int, int> AddThreeNumber () {return (x, y, z) ==> x + y + z;} partially applied version: public Func <int, int> AddThreeNumberCurrying () {Func <int, func <int, Func <int, int> addCurring = x => y => z => x + y + z; return addCurring;} call process: [Test] public void Three_number_add_test () {// Arrange var curringReasoning = new CurryingReasoning (); // Act var result1 = cur RingReasoning. addThreeNumber () (1, 2, 3); var curringResult = curringReasoning. addThreeNumberCurrying () (1); var curringResult2 = curringResult (2); var result2 = curringResult2 (3); // Assert result1.shocould (). be (6); result2.shocould (). be (6);} when there are too many function parameters, it is increasingly difficult to manually apply local functions. We can use the extension method to automatically apply local functions: 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, T2, T3, TResult> func) {return x => y => z => func (x, y, z );} in the same way, the Action <> signature function can also be automatically applied with these extension methods, which makes it easier to use the local method [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. shocould (). be (3);} Now, let's talk about it here. stackoverflow has several articles on the use cases and definitions of currying. You can continue to understand it. Function programming also has some important ideas. For example, pure function caching means that function calls are not affected by the outside world, the values obtained by calling the same parameters are always the same. Tail recursion, list, code is data (. net Expression Tree), some applications, combinations of functions, some of these ideas are still being learned, some are still thinking about their best use scenarios, so I will not sum up, if I understand my thoughts one day, I will supplement it. 4. Finally, I want to design a scenario and combine high-order functions, lambda expressions, and generic methods. I designed this example because there are many frameworks, open-source projects all have similar writing methods. It is precisely because of the combination of various technologies and ideas that they have very expressive and elegant code. Requirement: design a word Finder to check whether certain fields of an input model contain a word. Because different models have different fields, this search requires configuration, in addition, the smart prompts of vs can be fully utilized. This function is actually implemented in two ways: 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 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. shocould (). be (true);} the case itself is not practical, but we can see that it is the comprehensive application of various technologies that has designed extremely semantic If the function parameter is changed to Expression <Func <TModel, TProperty> type, we can also read the specific attribute name and other information.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.