Как вы реализуете goto в F #? - PullRequest
6 голосов
/ 20 августа 2011

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

 start:
 goto start

К сожалению, если я правильно понимаю ошибки компилятора, я не могу использовать этот же синтаксис в F #. Итак, поскольку он не поддерживается изначально, как я могу реализовать команду goto в F #?

Конечно, F # - достаточно мощный язык для реализации такой простой функции, как эта. Другие языки, такие как Javascript, которые изначально не поддерживают goto, все еще могут реализовать его через плагин.

Кроме того, я считаю, что F #, как один из языков в парадигме функционального программирования, должен поддерживать goto s более высокого уровня: где вы можете передать goto s goto s.

Ответы [ 5 ]

11 голосов
/ 20 августа 2011

Метка имеет много общего с функцией: они обе действуют как точки входа в некоторый код, который должен быть выполнен. Учитывая это сходство, вы можете сделать следующее:

let goto f = f()

let test() =
  let label = (fun () ->
    //rad code
    )
  //tight code
  goto label

Один незначительный недостаток заключается в том, что весь код должен быть заключен в замыкания. Я не знаю - не кажется слишком плохим для того, чтобы получить что-то такое же удобное как goto.

8 голосов
/ 20 августа 2011

К сожалению, если я правильно понимаю ошибки компилятора, я не могу использовать этот же синтаксис в F #.Итак, поскольку она не поддерживается изначально, как я могу реализовать команду goto в F #?

Как сказал Даниэль, метка и следующий блок инструкций могут быть преобразованы в функцию иего тело.Тогда каждый goto становится вызовом функции.Вы должны передать все локальные переменные в качестве аргументов, потому что отдельные функции имеют разные области видимости, и вы должны добавить сквозные вызовы из одного блока инструкций в следующий при необходимости.Однако хвостовые вызовы являются более общей концепцией.

Ваш пример цикла start становится следующим:

let rec start () =  // .start
  start()           // goto start

Обратите внимание, что приличный компилятор фактически скомпилирует этот эквивалентный высокоуровневый код обратно вjump / branch между блоками команд в ассемблере.Основное отличие состоит в том, что стековые фреймы должны быть реорганизованы, потому что вы можете выполнять вызов между совершенно разными средами.

Кроме того, я считаю, что F #, как один из языков в парадигме функционального программирования, должен бытьспособен поддерживать gotos более высокого уровня: где вы можете передавать gotos gotos.

Да, действительно.Вы не можете передавать метки на других языках, но вы можете передавать функции в F # как в качестве аргументов в вызовах функций, так и в качестве возвращаемых значений из функций.Другие языки, такие как Fortran, предлагают вычисленные goto как промежуточный дом.

Обратите внимание, что асинхронное программирование является важным практическим применением этой техники.Когда вы вызываете асинхронную функцию, вы сообщаете ей, где она должна перейти, когда она завершится.Например, когда вы звоните, чтобы начать асинхронную загрузку веб-страницы, вы передаете ей функцию, которую она будет вызывать, как только станут доступны данные (по сути, аппаратное прерывание, полученное, когда поступает последняя из ваших данных, в конечном итоге срабатывает с вашего максимума).-уровень управляемого кода для работы со свежими данными, что довольно круто).Современные языки дают вам инструменты для написания высокоуровневого асинхронного кода многократного использования, комбинируя эти goto -подобные методы с дополнительной генерацией кода во время компиляции.В других языках, таких как C #, вы облажались, потому что хотите обернуть несколько асинхронных вызовов в один try..catch, но не можете, потому что они фактически распределены по множеству различных функций.

8 голосов
/ 20 августа 2011

Вы можете получить поведение GOTO в F # с помощью взаимно рекурсивных функций.Оптимизация вызовов в хвосте допускает такой характер перехода и ничего не помещает в стек.

int parse() 
{
    Token   tok;

reading:
    tok = gettoken();
    if (tok == END)
        return ACCEPT;
shifting:
    if (shift(tok))
        goto reading;
reducing:
    if (reduce(tok))
        goto shifting;
    return ERROR;
}

Здесь do_read, do_shift и re_reduce действуют как метки.

type Token = END | SOMETHINGELSE

type Status = ACCEPT | ERROR

let parse gettoken shift reduce () =
    let rec do_read() =
        match gettoken() with
        | END -> ACCEPT
        | _ as tok -> do_shift tok

    and do_shift tok =
        if shift tok then
            do_read()
        else
            do_reduce tok

    and do_reduce tok =
        if reduce tok then
            do_shift tok
        else
            ERROR

    do_read()

Источник кода http://sharp -gamedev.blogspot.com / 2011/08 / забыто-потока управления-construct.html

5 голосов
/ 24 августа 2011

Один из подходов, который не упоминается в других ответах, заключается в создании собственного компоновщика вычислений.Я написал две статьи, которые реализуют некоторые обязательные функции для F # в компиляторе вычислений imperative { .. } (прочитайте первый и второй ).до реализации goto, но они реализуют continue и break.Вы можете добавить поддержку goto, но вы можете только перейти назад к меткам, которые были выполнены ранее.Вот пример использования continue:

imperative { 
  for x in 1 .. 10 do 
    if (x % 3 = 0) then do! continue
    printfn "number = %d" x }
1 голос
/ 21 августа 2011

Если вы посмотрите на исходный код , вы обнаружите, что goto.js реализован как текстовый препроцессор кода. Он использует регулярные выражения для поиска и замены меток и переходов на соответствующие конструкции Javascript.

Вы можете использовать этот подход для расширения любого языка (включая, конечно, F #). В .NET вы, вероятно, могли бы использовать T4 аналогичным образом. Тем не менее, манипулирование языком на текстовом уровне обычно является скорее хаком, чем правильным расширением (сам автор goto.js говорит: «Серьезно. Никогда не используйте это»), этот тип метапрограммирования часто вместо этого делается путем подключения к языку AST. .

Конечно, F # - достаточно мощный язык для реализации такой простой функции.

F # на самом деле очень плохо поддерживает метапрограммирование. Языки с хорошими возможностями метапрограммирования включают в себя любой Лисп, OCaml (через campl4 ), Haskell (через Template Haskell ), Nemerle , Scala , Boo . Traceur реализует надлежащие возможности Javascript AST для метапрограммирования. Но AFAIK пока нет ничего подобного для F #.

должен поддерживать более высокий уровень gotos

GOTO не являются первоклассными значениями ни на одном из известных мне языков, поэтому термин «GOTO более высокого уровня» не имеет смысла. Однако, если вы действительно заинтересованы в манипуляциях с управлением потоком в функциональных языках, вам следует изучить продолжения .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...