Петли в SML / NJ - PullRequest
       51

Петли в SML / NJ

5 голосов
/ 04 мая 2009

Я очень новичок в SNL / NJ, и мне было интересно, как я могу сделать следующее:

foo(stuff,counter)
{
   while(counter > 0)
   {
     bar(stuff);
     counter-1;
   }
   return;
}

Как-то так, но как мне уменьшить?:

foo(stuff,counter) = 
   while counter > 0 do bar(stuff) ??? // how do I decrement counter here?

Ответы [ 4 ]

7 голосов
/ 04 мая 2009

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

Если вы действительно хотите использовать мутации и циклы, вам нужно использовать структуру данных, называемую ссылкой, которая является своего рода «изменяемой ячейкой». Вы выделяете ссылку с помощью функции ref, передавая ей исходное содержимое. Вы получаете доступ к содержимому с помощью оператора !. И вы устанавливаете новое содержимое с помощью оператора :=. Таким образом, буквальный перевод вашего кода выше будет выглядеть примерно так: Как видите, синтаксис очень уродливый, и это еще одна причина, по которой люди его избегают.

fun foo (stuff, counter_start) =
let
  val counter = ref counter_start
in
  while !counter > 0 do (
    bar stuff;
    counter := !counter - 1
  )
end;
6 голосов
/ 04 мая 2009

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

Поскольку в вашем примере мутирующая вещь является простым параметром, вспомогательная функция не требуется. Ваш код становится

fun foo stuff counter =
  if counter > 0 then
    ( bar stuff
    ; foo stuff (counter-1)
    )
  else
    ()

Конечно, этот код все еще ужасно императив ... Вызов bar stuff выполняется исключительно для побочного эффекта. Не очень ML-иш.

5 голосов
/ 04 мая 2009

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

(ML допускает побочные эффекты в некоторых случаях, но для простоты давайте пока проигнорируем это)

Чего именно вы пытаетесь достичь? (Что нужно зациклить, что делают функции?

Если вы предоставите немного больше подробностей, мы можем более конкретно объяснить, как вы должны написать программу. Но в действительности ваша программа просто не имеет смысла в функциональном стиле.

2 голосов
/ 04 мая 2009

Я не знаю ML, но это какой-то ML-подобный псевдокод:

fun foo stuff 0 = return ()
  | foo stuff counter = (bar stuff; foo stuff (counter - 1))

Я не знаю, как «связывать» команды в ML; точка с запятой это просто заполнитель.

Как правило, вы не будете зацикливаться. Я предпочел бы ожидать обычные функции более высокого порядка. Когда вы привыкнете к этому, ручное написание цикла будет похоже на кодирование ассемблера.

редактировать: фиксированный код согласно комментарию

...