[Function] An article takes you to understand control flow, recursion, and higher-order functions

Table of contents

control flow

Conditional statements

iteration statement

Example: Prime Factorization

 

recursion

Example: Factorial

Example: Fibonacci Sequence

Example: Judging odd and even numbers

higher order functions

lambda expression

design function

Example: cumulative calculation

Example: Currying

Lab 1: Functions, Control

Homework 2: Higher Order Functions

Project 1: The Game of Hog


control flow

Statements executed by the interpreter to perform certain operations.

For example, the entire compound statement (compound statement) is  def declared in Python; the header  header determines the type of a simple statement (clause), which is followed by a statement sequence (suite). The interpreter will execute this sequence of statements in a certain order.

Conditional statements

Conditional statements appear as keywords in most languages  if .

In Python  True , and  False represent true or false, respectively, if leads a conditional statement and its true branch, zero or one else leads to a false branch, and there may be zero or more elifs for nesting.

def absolute_value(n):
    if n < 0:
        return -n
    elif n == 0:
        return 0
    else:
        return n

 In the scheme  #t and  #f represent true or false respectively, elif cannot be nested in terms of grammar (if test consequent alternative)

(define (absolute-value n)
  (if (positive? n) n (- n)))

FP generally provides a set of guard-like syntax, that is, the conditional statement can accept any number of conditional judgments, and the conditional judgment is performed from top to bottom. When the condition is true, the statement block is executed and the conditional statement is exited. Conforms will have a default block for wrapping around. In fact, this statement is more like  if-then-elif-else a variant of .

(cond ((< n 0) (- n))
      ((= n 0) n)
      (else n))

In erlang, if is cond.

absolute_value(N) ->
    if
        N < 0 -> -N;
        N =:= 0 -> N;
        true -> N
    end.

iteration statement

The iteration statement is also called the loop statement, which runs the loop body when the condition is met, and exits until the condition is not satisfied.

i = 0
total = 0
while i < 3:
    total = total + i
    i = i + 1
    print(total)

print(total)
# 0
# 1
# 3

The loop in the scheme is a bit different from the while in the normal language, although it is more like the for in the C language (or maybe the for in C is more like the do in Lisp), no matter how you say it, it is a variable change loop statement.

(do ((i 0 (+ i 1)) (total 0 (+ total i))) ;; assignment
    ((> i 2) total) ;; exit
  (display total) ;; body
  (newline))
;; 0
;; 0
;; 1
;; 3 -- not print

 As a pure FP, erlang does not have a while statement available, and needs to be simulated using tail recursion.

loop(3, Total) -> Total;
loop(I, Total) -> loop(I + 1, Total + I).

 

Example: Prime Factorization

Prime factorization is a typical problem that needs to use loops and conditions to find the answer. For each positive integer N, we can decompose the set of all its prime factors:

  • 8 = 2 * 2 * 2
  • 9 = 3 * 3
  • 10 = 2 * 5
  • 11 = 11

A better way is to find the smallest prime factor of this positive integer, and then continue to decompose the remaining part until the decomposition is complete.

858 = 2∗429 = 2∗3∗143 = 2∗3∗11∗13

(define (get-primes n)
  (let ((bits (make-vector (+ n 1) #t)))
    (let loop ((p 2) (ps '()))
      (cond ((< n p) (reverse ps))
            ((vector-ref bits p)
             (do ((i (+ p p) (+ i p))) ((< n i))
               (vector-set! bits i #f))
             (loop (+ p 1) (cons p ps)))
            (else (loop (+ p 1) ps))))))

(define (get-factorization n primes)
  (let ((prime (car primes))
        (others (cdr primes)))
    (if (= (remainder n prime) 0)
        prime
        (get-factorization n others))))

(define (prime-factorization n)
  (if (< n 3)
      (list n)
      (let ((primes (get-primes n)))
        (let loop ((num n) (ans '()))
          (cond ((= num 1) (reverse ans))
                (else (let ((prime (get-factorization num primes)))
                        (loop (quotient num prime) (cons prime ans)))))))))

 

recursion

Recursion In mathematics and computer science, the method of using a function itself in its definition. The term recursion is also more commonly used to describe the process of repeating things in a self-similar way. For example, when two mirrors are approximately parallel to each other, images nested in the mirrors appear in an infinite recursion. It can also be understood as a process of self-replication.

Here's an explanation that might be more helpful for understanding the recursive process:

  1. Are we done yet? If done, return the result. Without such a terminating condition, the recursion would continue forever.
  2. If not, simplify the problem, solve the easier problem, and assemble the result into a solution to the original problem. Then return to that solution.

Example: Factorial

Factorial(0)=1,

Factorial(1)=1,

Factorial(N)=N×Factorial(N−1)

(define (factorial n)
  (cond ((= n 0) 1)
        (else (* n (factorial (- n 1))))))

 In this calculation process, through the substitution model, it can be seen that the calculation is a shape that is gradually expanded and then contracted. The calculation process constructs a formed 推迟进行的操作chain, and the shrinking stage is the actual execution of these operations. This calculation process is called for 递归计算过程. If this process is to be executed, the interpreter must maintain the trajectory of the operations to be performed in the future. In this example, the length of the multiplication chain that is deferred is the amount of information that needs to be saved to save its trajectory. This length increases with the value of n And linear growth, this process is called linear recursive computation .

(factorial 5)
(* 5 (factorial 4))
(* 5 (* 4 (factorial 3)))
(* 5 (* 4 (* 3 (factorial 2))))
(* 5 (* 4 (* 3 (* 2 (factorial 1)))))
(* 5 (* 4 (* 3 (* 2 (* 1 (factorial 0))))))
(* 5 (* 4 (* 3 (* 2 (* 1 1)))))
(* 5 (* 4 (* 3 (* 2 1))))
(* 5 (* 4 (* 3 2)))
(* 5 (* 4 6))
(* 5 24)
120

 Let's look at another implementation

(define (factorial n)
  (let factorial-iter ((product 1) (counter 1))
    (if (> counter n)
        product
        (factorial-iter (* counter product)
                        (1+ counter)))))

 There is no growth or contraction in this calculation process. At each step of the calculation process, the trajectory that needs to be saved is the current value of the variable  product sum  counter . We call this process an iterative calculation process . An iterative calculation process is a calculation process whose state can be described by a fixed number of state variables. At the same time, there is a set of fixed rules that describe how the state variables are updated when the calculation process transitions from one state to another. There is also a The detection of the end state is used to describe how the calculation process is terminated.

(factorial-iter   1 1 5)
(factorial-iter   1 2 5)
(factorial-iter   2 3 5)
(factorial-iter   6 4 5)
(factorial-iter  24 5 5)
(factorial-iter 120 6 5)
120

 

Example: Fibonacci Sequence

Fibonacci(0)=0,

Fibonacci(1)=1,

Fibonacci(N)=Fibonacci(N−1)+Fibonacci(N−2)

It can be seen that the Fibonacci sequence is a naturally recursive function, and the function recursion has been encountered in the previous code, just look at the code implementation.

(define (fibonacci n)
  (cond ((= n 0) 0)
        ((= n 1) 1)
        (else (+ (fibonacci (- n 1))
                 (fibonacci (- n 2))))))

If you draw the call graph of the fibonacci function, you can see that it looks like a tree, and such recursion is called  tree recursion . But there are a lot of repeated calculations, which will lead to meaningless calculations and waste CPU performance.

 

Because this method of calculating the Fibonacci sequence is very bad, it does a lot of redundant calculations, and the number of recursions increases exponentially with the size of n, so we need to use an iterative method to optimize the solution process.

(define (fibonacci n)
  (let fibonacci-iter ((a 1) (b 0) (counter 1))
    (if (> counter n)
        b
        (fibonacci-iter (+ a b) a (+ counter 1)))))

 Tree recursive computation procedures are not useless. When considering operations on hierarchically structured data rather than logarithmic operations, tree recursive computation procedures are a natural and powerful tool for understanding and designing programs. .

Example: Judging odd and even numbers

If you can't directly use the method of modulo to determine odd or even numbers, then there is a simple and clear way - ask whether the previous number is odd/even. Obviously this is a recursive question, and we need to keep asking forward until we get an answer.

(define (odd? n)
  (if (= n 0)
      #f
      (even? (- n 1))))
(define (even? n)
  (if (= n 0)
      #t
      (odd? (- n 1))))

 This way of having multiple functions calling each other recursively is called indirect recursion.

higher order functions

Higher-order functions  (Higher-Order Functions) are functions that pass functions as parameters and return values. This feature mostly occurs in languages ​​​​with FP features, and often these languages ​​​​also provide lambdas at the same time.

lambda expression

 A lambda expression  is a simplified way of defining a function that captures some variables in the current environment, also known as a closure. Lambda often appears with higher-order functions, usually when passing a conditional predicate, create a corresponding lambda object instead of creating a function pass.

(lambda (x)
  (= x 0))

A lambda is no different from a normal function and consists of three parts –  a header  (the lambda keyword), a parameter list , and a function body . In addition, lambda cannot be recursive. If you want to recurse lambda, you need to use the Y combinator. 1  ,  2

Environment variables captured in lambda can be used directly

(define (zero? x)
  ((lambda () (= x 0))))
(zero? 0) ;; #t
(zero? 1) ;; #f

design function

For a function, when designing a function, you need to pay attention to three aspects:

  1. Each function should have only one precise task, and functions that perform multiple tasks should be split into multiple functions
  2. Don't repeat yourself  (DRY, Don't repeat yourself), if you ever find yourself copy-pasting a piece of code, you've probably spotted an opportunity to abstract it with functions
  3. The function should be designed to be more general, such as not providing  square  and  cube , but providing  pow , and specifying the power to realize square and cube respectively

Example: cumulative calculation

For example, you now need to calculate the accumulation, including but not limited to \sum_{i=1}^{n}{i}, \sum_{i=1}^{n}{i^{2}} etc., according to the three aspects of the design function, we need to design a function for accumulation! In addition, this function needs to be abstract enough to provide generality.

Then I can define two parameters startand endare used to identify the upper and lower limits of the accumulation function, so the most important thing is how to accumulate and how to tell this function? Design this function as a higher order function!

 

(define (summation start end term)
  (let summation-iter ((counter start) (value 0))
    (if (> counter end)
        value
        (summation-iter (+ counter 1) (+ value (term counter))))))
(summation 0 10 (lambda (x) x))  ;; sum (i), 55
(summation 0 10 (lambda (x) (* x x))) ;; sum (i^2), 385
(summation 0 10 (lambda (x) (sqrt x))) ;; sum (sqrt(i)), 22.4682

Example: Currying

Currying (currying) is an important feature of mathematics and FP. It is a technique of converting a function that receives multiple parameters into a function that receives one parameter, and returns a new function that receives the remaining parameters and returns the result. So these three expressions are equivalent.

 

(define (sum a b) (+ a b))
(define (sum-curry a) (lambda (b) (+ a b)))
(define add10 (sum-curry 10))
(add10 5)  ;; 15
(sum 10 5) ;; 15

Lab 1: Functions, Control

(define (falling n k)
  "Compute the falling factorial of n to depth k."
  (define factorial
    (lambda (n)
      (cond ((= n 0) 1)
            ((= n 1) 1)
            (else (* n (factorial (- n 1)))))))
  (/ (factorial n)
     (factorial (- n k))))

 

(define (sum-digits n)
  "Sum all the digits of y."
  (let sum-digits-iter ((num n) (val 0))
    (if (= num 0)
        val
        (sum-digits-iter (quotient num 10) (+ val (remainder num 10))))))

(define (double-eights n)
  "Return true if n has two eights in a row."
  (let double-eights-iter ((num n) (prev #f))
    (if (= num 0)
        #f
        (let ((curr (= (remainder num 10) 8)))
          (or (and curr prev)
              (double-eights-iter (quotient num 10) curr))))))

Homework 2: Higher Order Functions

product

         calculateterm(1) \times term(2) \times \cdots \times term(n)

(define (product n term)
  "Return the product of the first n terms in a sequence."
  (let product-iter ((counter 1) (init 1))
    (if (> counter n)
        init
        (product-iter (+ counter 1) (* init (term counter))))))

 

accumulate

         accumulation function        

(define (accumulate merger init n term)
  "Return the result of merging the first n terms in a sequence and start.
    The terms to be merged are term(1), term(2), ..., term(n). merger is a
    two-argument commutative function."
  (let accumulate-iter ((counter 1) (value init))
    (if (> counter n)
        value
        (accumulate-iter (+ counter 1) (merger value (term counter))))))

 

Project 1: The Game of Hog

I know! I’ll use my Higher-order functions to Order higher rolls.

In Hog, two players take turns trying to get closer to the goal, and the first player to total at least the goal wins, with a default goal of 100 points. In each attempt, the player selects up to ten dice to roll. The player's score is the sum of all dice rolls for the current round. A player runs a certain risk if they roll too many dice:

  • Sow Sad , if the result of any dice is 1, the current player's turn score is 1.

In a normal Hog game, that's all there is to it. In order to add some game features, we will add some special rules:

  • Pig Tail , a player who chooses to roll 0 dice will 2 \times \lvert{tens - ones}\rvert + 1 score points . where tensand onesrefer to the tens and ones digits of the opponent's score.

  • Square Swine , when a player gets points on his turn and the final result is a perfect square, set his score to the next perfect square.

Guess you like

Origin blog.csdn.net/qq_62464995/article/details/128474790