[Função] Um artigo leva você a entender o fluxo de controle, a recursão e as funções de ordem superior

Índice

controle de fluxo

Declarações condicionais

declaração de iteração

Exemplo: Fatoração Primária

 

recursão

Exemplo: Fatorial

Exemplo: Sequência de Fibonacci

Exemplo: julgar números pares e ímpares

funções de ordem superior

expressão lambda

função de design

Exemplo: cálculo cumulativo

Exemplo: curry

Laboratório 1: Funções, Controle

Lição de casa 2: funções de ordem superior

Projeto 1: O Jogo do Porco


controle de fluxo

Declarações executadas pelo interpretador para realizar determinadas operações.

Por exemplo, toda a instrução composta (instrução composta) é  def declarada em Python; o cabeçalho  header determina o tipo de uma instrução simples (cláusula), que é seguida por uma sequência de instruções (suite). O interpretador executará essa sequência de instruções em uma determinada ordem.

Declarações condicionais

As declarações condicionais aparecem como palavras-chave na maioria dos idiomas  if .

Em Python  True , e  False representam verdadeiro ou falso, respectivamente, se leva a uma instrução condicional e sua ramificação verdadeira, zero ou um senão leva a uma ramificação falsa e pode haver zero ou mais elifs para aninhamento.

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

 No esquema  #t e  #f representam verdadeiro ou falso respectivamente, elif não pode ser aninhado em termos de gramática (if test consequent alternative)

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

FP geralmente fornece um conjunto de sintaxe tipo guarda, ou seja, a declaração condicional pode aceitar qualquer número de julgamentos condicionais, e o julgamento condicional é executado de cima para baixo. Quando a condição é verdadeira, o bloco de declarações é executado e o condicional declaração é encerrada. Conforms terá um bloco padrão para agrupar. Na verdade, esta declaração é mais como  if-then-elif-else uma variante de .

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

Em erlang, se é cond.

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

declaração de iteração

A instrução de iteração também é chamada de instrução de loop, que executa o corpo do loop quando a condição é atendida e sai até que a condição não seja satisfeita.

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

print(total)
# 0
# 1
# 3

O loop no esquema é um pouco diferente do while na linguagem normal, embora seja mais parecido com o for na linguagem C (ou talvez o for em C seja mais parecido com o do em Lisp), não importa como você o diga , é uma instrução de loop de alteração de variável.

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

 Como um FP puro, o erlang não tem uma instrução while disponível e precisa ser simulado usando recursão de cauda.

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

 

Exemplo: Fatoração Primária

A fatoração primária é um problema típico que precisa usar loops e condições para encontrar a resposta. Para cada inteiro positivo N, podemos decompor o conjunto de todos os seus fatores primos:

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

Uma maneira melhor é encontrar o menor fator primo desse inteiro positivo e continuar a decompor a parte restante até que a decomposição seja concluída.

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)))))))))

 

recursão

Recursão Em matemática e ciência da computação, o método de usar a própria função em sua definição. O termo recursão também é mais comumente usado para descrever o processo de repetição de coisas de maneira auto-semelhante. Por exemplo, quando dois espelhos são aproximadamente paralelos entre si, as imagens aninhadas nos espelhos aparecem em uma recursão infinita. Também pode ser entendido como um processo de auto-replicação.

Aqui está uma explicação que pode ser mais útil para entender o processo recursivo:

  1. Já terminamos? Se feito, retorne o resultado. Sem essa condição de terminação, a recursão continuaria para sempre.
  2. Caso contrário, simplifique o problema, resolva o problema mais fácil e reúna o resultado em uma solução para o problema original. Em seguida, retorne a essa solução.

Exemplo: Fatorial

Fatorial(0)=1,

Fatorial(1)=1,

Fatorial(N)=N×Fatorial(N−1)

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

 Nesse processo de cálculo, por meio do modelo de substituição, pode-se perceber que o cálculo é uma forma que se expande gradativamente e depois se contrai. O processo de cálculo constrói uma cadeia formada 推迟进行的操作, e a etapa de encolhimento é a própria execução dessas operações. Esse cálculo processo é chamado 递归计算过程. Se este processo for executado, o interpretador deve manter a trajetória das operações a serem realizadas no futuro. Neste exemplo, o comprimento da cadeia de multiplicação que é adiada é a quantidade de informação que precisa ser salva para salvar sua trajetória Este comprimento aumenta com o valor de n E crescimento linear, este processo é chamado de computação recursiva linear .

(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

 Vejamos outra implementação

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

 Não há crescimento ou contração neste processo de cálculo. Em cada etapa do processo de cálculo, a trajetória que precisa ser salva é o valor atual da  product soma  variável counter . Chamamos esse processo de processo de cálculo iterativo . Um processo de cálculo iterativo é um processo de cálculo cujo estado pode ser descrito por um número fixo de variáveis ​​de estado. Ao mesmo tempo, há um conjunto de regras fixas que descrevem como as variáveis ​​de estado são atualizadas quando o processo de cálculo passa de um estado para A detecção do estado final é usada para descrever como o processo de cálculo é finalizado.

(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

 

Exemplo: Sequência de Fibonacci

Fibonacci(0)=0,

Fibonacci(1)=1,

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

Pode-se ver que a sequência de Fibonacci é uma função naturalmente recursiva, e a recursão da função foi encontrada no código anterior, basta olhar para a implementação do código.

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

Se você desenhar o gráfico de chamadas da função de Fibonacci, poderá ver que ele se parece com uma árvore, e essa recursão é chamada de  recursão de árvore . Mas há muitos cálculos repetidos, que levarão a cálculos sem sentido e desperdiçarão o desempenho da CPU.

 

Como esse método de cálculo da sequência de Fibonacci é muito ruim, faz muitos cálculos redundantes e o número de recursões aumenta exponencialmente com o tamanho de n, então precisamos usar um método iterativo para otimizar o processo de solução.

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

 Os procedimentos de computação recursiva em árvore não são inúteis.Ao considerar operações em dados estruturados hierarquicamente em vez de operações logarítmicas, os procedimentos de computação recursiva em árvore são uma ferramenta natural e poderosa para entender e projetar programas.

Exemplo: julgar números pares e ímpares

Se você não pode usar diretamente o método do módulo para determinar números ímpares ou pares, existe uma maneira simples e clara - pergunte se o número anterior é ímpar/par. Obviamente, esta é uma pergunta recursiva e precisamos continuar perguntando até obter uma resposta.

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

 Essa maneira de ter várias funções chamando umas às outras recursivamente é chamada de recursão indireta.

funções de ordem superior

Funções de ordem superior  (Higher-Order Functions) são funções que passam funções como parâmetros e retornam valores. Esse recurso ocorre principalmente em linguagens com recursos de FP e, muitas vezes, essas linguagens também fornecem lambdas ao mesmo tempo .

expressão lambda

 Uma expressão lambda  é uma forma simplificada de definir uma função que captura algumas variáveis ​​no ambiente atual, também conhecido como encerramento. Lambda geralmente aparece com funções de ordem superior, geralmente ao passar um predicado condicional, crie um objeto lambda correspondente em vez de criar um passe de função.

(lambda (x)
  (= x 0))

Um lambda não é diferente de uma função normal e consiste em três partes –  um cabeçalho  (a palavra-chave lambda), uma lista de parâmetros e um corpo de função . Além disso, lambda não pode ser recursivo.Se você quiser recursivamente lambda, você precisa usar o combinador Y. 1  ,  2

Variáveis ​​de ambiente capturadas em lambda podem ser usadas diretamente

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

função de design

Para uma função, ao projetar uma função, você precisa prestar atenção a três aspectos:

  1. Cada função deve ter apenas uma tarefa precisa, e as funções que executam várias tarefas devem ser divididas em várias funções
  2. Não se repita  (DRY, Don't repeat yourself), se você já se pegou copiando e colando um trecho de código, provavelmente viu uma oportunidade de abstraí-lo com funções
  3. A função deve ser projetada para ser mais geral, como não fornecer  square  e  cube , mas fornecer  pow e especificar a potência para realizar square e cube respectivamente

Exemplo: cálculo cumulativo

Por exemplo, agora você precisa calcular a acumulação, incluindo, mas não limitado a \sum_{i=1}^{n}{i}, \sum_{i=1}^{n}{i^{2}} etc., de acordo com os três aspectos da função de design, precisamos projetar uma função de acumulação! Além disso, essa função precisa ser abstrata o suficiente para fornecer generalidade.

Então eu posso definir dois parâmetros starte endsão usados ​​para identificar os limites superior e inferior da função de acumulação, então o mais importante é como acumular e como contar essa função? Projete esta função como uma função de ordem superior!

 

(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

Exemplo: curry

Currying (currying) é um recurso importante da matemática e do FP. É uma técnica de converter uma função que recebe vários parâmetros em uma função que recebe um parâmetro e retorna uma nova função que recebe os parâmetros restantes e retorna o resultado. Portanto, essas três expressões são equivalentes.

 

(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

Laboratório 1: Funções, Controle

(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))))))

Lição de casa 2: funções de ordem superior

produtos

         calcularterm(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))))))

 

acumular

         função de acumulação        

(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))))))

 

Projeto 1: O Jogo do Porco

Eu sei! Usarei minhas funções de ordem superior para solicitar rolos superiores.

No Hog, dois jogadores se revezam tentando chegar mais perto do gol, e o primeiro jogador a somar pelo menos o gol vence, com meta padrão de 100 pontos. Em cada tentativa, o jogador seleciona até dez dados para rolar. A pontuação do jogador é a soma de todas as jogadas de dados da rodada atual. Um jogador corre um certo risco se rolar muitos dados:

  • Sow Sad , se o resultado de qualquer dado for 1, a pontuação do turno do jogador atual é 1.

Em um jogo normal do Hog, isso é tudo. Para adicionar alguns recursos do jogo, adicionaremos algumas regras especiais:

  • Pig Tail , um jogador que escolhe rolar 0 dados 2 \vezes \lvert{dezenas - unidades}\rvert + 1 marcará pontos . onde tense onesreferem-se às dezenas e unidades da pontuação do adversário.

  • Square Swine , quando um jogador ganha pontos em seu turno e o resultado final é um quadrado perfeito, defina sua pontuação para o próximo quadrado perfeito.

Acho que você gosta

Origin blog.csdn.net/qq_62464995/article/details/128474790
Recomendado
Clasificación