Затенение и Вложенная функция - PullRequest
8 голосов
/ 19 марта 2010

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

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 ]

16 голосов
/ 19 марта 2010

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

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

Помогает ли это?

13 голосов
/ 19 марта 2010

Я думаю, @ 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 в качестве аргумента. Итак, главное, что интересно во вложенных функциях, это то, что они могут получать доступ к значениям, объявленным внешней функцией.

...