Squeak.ru - шаблоны программирования

Неместные и местные доходы: вопрос уровня реализации?

В последнее время я немного поиграл с Groovy и был удивлен, увидев, что он не поддерживает нелокальные возвраты изнутри замыкания. Я был весьма удивлен этим, потому что всегда ожидал, что это будет нормальным случаем со времен разработки Smalltalk. Лямбды JDK8 также не поддерживают нелокальные возвраты, как я понял к своему отчаянию. К счастью, у Scala есть.

Вопрос. Являются ли нелокальные возвраты изнутри замыкания обязательным условием для того, чтобы реализация замыкания была "полноценной"? Или я просто привык к этому из Smalltalk, но это не так.

Иллюстрация кода

def list = list(1, 2, 3)
def value = list.forEach { each ->
    println(each)
        if(true)
             return each
    return 5
}

println(value)

Я ожидаю, что он напечатает «11», а не «1235». По крайней мере, он не должен компилироваться, если будет печатать «1235».


  • В чем именно вопрос? Некоторые люди согласятся с вами, а некоторые нет. Что вы получите? Если вы считаете это препятствием на пути к Groovy, не используйте его. 16.07.2013
  • Что ж, в конце концов, все сводится к вопросу о том, что закрытие должно поддерживать, чтобы быть закрытием? Является ли выражение, открывающее доступ к свободной переменной, уже закрытием? Или он должен поддерживать нелокальный возврат, чтобы не быть фальшивым закрытием? 16.07.2013

Ответы:


1

Не нашел данных для поддержки меня, но вот мой 0,02: закрытие должно возвращаться только от себя, а не от родительского вызывающего. Возврат от родительского вызывающего объекта - это та же концепция, что и в блоках ruby ​​(1): управление / область действия обоих блоков (вызываемого и вызывающего) одинаковы, в то время как закрытие должно быть само по себе.

scala это синтаксический сахар, чтобы сделать это в Groovy: «вернуться» из закрытия, выбрасывая исключение.

Чтобы «вернуться» из закрытия в Groovy, придерживайтесь find, findAll или findResult (согласно ответу @jesseplymale): они повторяются через правые элементы.

...

Я не гуру в javascript, но, похоже, он со мной согласен:

// prints every item; the return is only from the function
[1, 2, 3, 4, 5].forEach( function(item) {
  console.log(item);
  return item;
}) 

Ruby с блоками и лямбдами:

# block: prints "1" and stop iterating
def a()
  [1, 2, 3, 4, 5].each { |it|
    puts it
    return it 
  }
end

a()

Передача блока и лямбды в качестве параметра:

# this method receives a block/lambda and passes 
# it to the 'each' method from list
def b(block)
  [1, 2, 3, 4, 5].each &block
end

# this block fails with "unexpected return (LocalJumpError)"
#b(Proc.new { |it| puts it; return it; }) 

# this lambda keeps iterating and printing, even though 
# it has the return statement
b(lambda { |it| 
  puts it
  return it
})

(1): Обратите внимание, я не гуру в Ruby, и это может быть более или менее неправильно.

16.07.2013
  • Большое спасибо, Уилл. Я написал то же самое на Scala и был доволен, что Scala ведет себя так же, как я привык к Smalltalk. Действительно неприятно видеть, что Scala прибегает к выбросу исключения только для нелокального возврата. Но это была хорошая информация! Интересно, несет ли это JVM ... 16.07.2013
  • Полагаю, что так. Я думаю, что большинство замыканий / лямбда-выражений / SAM реализуются с использованием анонимных классов, а не реальных циклов. Может быть, если бы компилятор смог превратить это в метод ... 16.07.2013
  • Википедия en.wikipedia.org/wiki/Closure_%28computer_science%29 говорит: несколько слов о локальном и не локальном / возврате, и кажется, что поведение Ruby Proc и лямбда отличается от этого POV 16.07.2013
  • Википедия @WillP сообщает, что возврат Proc будет нелокальным, а лямбда-возврат будет локальным, и дает пример. Но я не проверял ... 17.07.2013

  • 2

    Вам нужен list.findResult () вместо list.each ().

    def list = [1, 2, 3]
    def value = list.findResult { each ->
      println(each)
      if(each)
        return each
      return 5
    }
    println(value)
    // prints 1<newline>1
    
    15.07.2013
  • Итак, Groovy поддерживает нелокальный возврат из метода findResult. Это хорошие новости. Тем не менее, код, который я показал, должен был работать, как ожидалось, или не должен был компилироваться. Так что я не совсем уверен, за что именно были занижены оценки. Во всяком случае, вопрос был в том, что компенсирует закрытие. Являются ли неместные возвраты или несущественным вопросом, без которого закрытие не является настоящим закрытием, или это скорее изящность? Интересно, есть ли какие-либо установленные определения того, что такое закрытие. То, что я нашел в Интернете, довольно расплывчато ... Спасибо, Оливер 16.07.2013
  • Этот код Scala компилируется, но затем также выводит 11, как и ожидалось: object Foo {def main (args: Array [String]) {println (bar)} def bar: Int = {val list = List (1, 2, 3) list. foreach {each = ›if (true) {return each} 5}} 16.07.2013
  • @OliverPlow Каждый Groovy будет продолжаться до тех пор, пока все элементы не будут повторены. Итак, ваш аргумент действительно связан с each против foreach, а не с замыканиями как таковыми. 16.07.2013
  • @TimYares: Спасибо, Тим. Но я не понимаю твою точку зрения. Кажется, что в коллекции нет итератора foreach, а также нет ключевого слова языка. Вы имеете в виду простой цикл for? 16.07.2013
  • Еще я не понимаю, почему можно вернуться из findResult, но не из forEach. Так что вся проблема не в закрытии ?! Я немного запутался ... 16.07.2013
  • Потому что findResult останавливается, когда находит первый результат, а each выполняет итерацию по каждому элементу в коллекции. Это семантика метода, а не проблема закрытия 16.07.2013
  • Groovy также предоставляет _1 _ (обратите внимание на s на findResults()), который не останавливается, когда находит первый результат. Он отбрасывает любые возвращаемые значения, которые являются нулевыми. 20.07.2013

  • 3

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

    Я думаю, что Smalltalkers очень гордятся (и это справедливо) своим закрытием. Однажды я слышал, что «блоки Smalltalk сделали больше для лямбда, чем любой другой спортивный язык лямбда».

    В большинстве языковых справочников есть раздел, посвященный «управлению потоком». Но Smalltalk - единственный язык, который я знаю, который осмелился не только выполнять «объекты полностью вниз», но и «полностью закрывать объекты». Все управление потоком (циклы, логические ветвления и т. Д.) В Smalltalk осуществляется с помощью замыканий (или, по крайней мере, кажется, что это так, не обращайте внимания на этот оптимизирующий компилятор за занавеской!).

    26.07.2013

    4

    Лексическое замыкание с локальным возвратом - самый обычный способ реализации лямбда-выражений. На самом деле нелокальный возврат в блоках Smalltalk / Ruby - исключение для большинства других языков. Некоторые Smalltalks на самом деле даже не закрывают блоки сверх лексической области видимости, поэтому вы можете задаться вопросом, в какой степени блоки можно рассматривать как замыкания.

    Если бы вы перешли на Smalltalk / Ruby из Scheme (как это сделал я), ваш вопрос мог бы быть противоположным. То есть: «Должны ли блоки Smalltalk иметь локальный возврат, чтобы считаться« полностью закрытыми »?»

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

    Нелокальные возвраты не являются неотъемлемой частью замыканий, это просто особый случай, и они могут быть реализованы независимо (овеществленные продолжения, исключения, другие специальные конструкции).

    См. Эту (старую) статью: http://smalltalk.gnu.org/blog/s11001001/lexical-block-return-language-crux.

    Вот соответствующее обсуждение замыканий и возвратов: http://lambda-the-ultimate.org/node/2009

    [редактировать]

    Нашел этот связанный вопрос:

    В чем именно разница между закрытием и заблокировать?

    01.12.2014
    Новые материалы

    День 76/100 Книга
    День 76/100 Книга Обучение — это непрерывный процесс с ресурсами, широко используемыми людьми, которые признают сложный путь достижения успешной цели. Хотя поначалу это может показаться..

    Gmail Smart Compose: умный способ написать электронное письмо
    Gmail означает почту Google, это бесплатная служба электронной почты, предоставляемая Google по всему миру. Google официально запустил Gmail в 2004 году, и в настоящее время этой услугой..

    API следующего поколения: gRPC n Javascript
    gRPC - быстро развивающаяся технология, которая потенциально может заменить протокол HTTP. Это быстрее, чем традиционные HTTP-вызовы, потому что он отправляет данные в двоичном формате, а не в..

    Ускоренный курс по жадным алгоритмам
    Дай мне, дай мне, дай мне Аудитория Эта статья предназначена для инженеров, достаточно хорошо разбирающихся в программировании, структурах данных и алгоритмах. Он в основном основан на..

    Изучайте Java (Урок 2: Комментарии)
    Как и зачем использовать комментарии в java с примерами Введение: Комментарии — это строки текста в программе Java, которые игнорируются компилятором и используются для добавления..

    Async Await в Swift: легкое управление параллелизмом
    Введение в запуск асинхронного кода Асинхронный код выполняет несколько операций одновременно. Параллелизм необходим при выполнении тяжелых вычислений или сетевых запросов. Однако это может..

    Очистить файлы Program .cache в Ubuntu 20.10
    Очистите кеш за несколько простых шагов! GNU / Linux реализовал эффективное управление хранилищем для своих пользователей. Но заметили ли вы, что в вашей системе Linux заканчивается место,..