Затенение и Вложенная функция

Я хочу понять, как работают механизмы Shadowing и Nested. Например:

let func y =
    let dup y = y + y
    let z = dup y
    let dup y =
        let dup z =
            let y = y * z
            y
        let z = y
        y
    dup z + z;;

val func : int -> int

> func 3;;
val it : int = 12

Может кто-нибудь объяснить, что здесь происходит?

2 ответа

Решение

Ваш код эквивалентен следующему, где я просто пронумеровал экземпляры ваших имен, чтобы помочь вам визуализировать, как происходит теневое копирование.

let func y0 = 
  let dup0 y1 = y1 + y1
  let z0 = dup0 y0
  let dup1 y2 = 
    let dup2 z1 = 
      let y3 = y2 * z1 
      y3
    let z2 = y2 
    y2 
  dup1 z0 + z0

Конечно, это может быть еще больше упрощено. поскольку dup2 а также z2 никогда не используются, dup1 эквивалентно let dup1 y2 = y2и вся функция эквивалентна

let func y0 =
  let dup0 y1 = y1 + y1
  let z0 = dup0 y0
  dup1 z0 + z0

Что эквивалентно

let func y0 =
  let z0 = y0 + y0
  z0 + z0

заменой Это так же, как

let func y0 = 4 * y0

Это помогает?

Я думаю, что @kvb дает очень хорошее объяснение, показывающее, как код оценивает. Код комбинирует вложенные функции и теневое копирование довольно запутанно:-). Я думаю, что полезно взглянуть на две концепции отдельно.

Затенение позволяет скрыть значение за новым значением в let декларация или обязательное значение в match построить. Это означает, что вы больше не сможете получить доступ к исходному значению. Вот более простой пример:

let foo num =
  let num = num + 20 // Line 2
  let num = num * 2  // Line 3
  num

Здесь мы объявляем функцию, которая принимает аргумент с именем num, Допустим, мы вызываем функцию с 1 в качестве аргумента. Во второй строке мы объявляем новое значение с тем же именем - инициализированное значение 1 + 20, равное 21. Третья строка снова объявляет новое значение и инициализирует его как 21 * 2 (потому что она видит последний объявленный символ num, который имеет значение 21). В строке 4 мы снова получаем доступ к последнему объявленному символу num и возвращаем 42.

Это полезно, главным образом, когда у вас есть какое-то вычисление, которое вычисляет новое значение, которое должно использоваться всеми последующими вычислениями. Затенение позволяет скрыть предыдущее значение, поэтому нет опасности, что вы случайно будете использовать исходное значение.

Вложенные функции весьма полезны, когда вам нужно выполнить некоторое локальное вычисление утилит, использующее параметры внешней функции. Например:

let times x nums = 
  let timesUtil y = y * x
  for n in nums do
    printfn "%d" (timesUtil n)

Здесь мы объявляем служебную вложенную функцию timesUtil который умножает любое число на значение x (что является аргументом times функция). Затем мы можем использовать его позже (в последней строке), чтобы выполнить операцию, не пропуская x значение в качестве аргумента снова. Итак, главное, что интересно во вложенных функциях, это то, что они могут получить доступ к значениям, объявленным внешней функцией.

Другие вопросы по тегам