Список фрагментов функционального кода для процедурных программистов? [закрыто]

Использование «setInterval» & amp; «clearInterval» устраняет проблему:

function drawMarkers(map, markers) {
    var _this = this,
        geocoder = new google.maps.Geocoder(),
        geocode_filetrs;

    _this.key = 0;

    _this.interval = setInterval(function() {
        _this.markerData = markers[_this.key];

        geocoder.geocode({ address: _this.markerData.address }, yourCallback(_this.markerData));

        _this.key++;

        if ( ! markers[_this.key]) {
            clearInterval(_this.interval);
        }

    }, 300);
}
16
задан 3 revs 7 May 2009 в 21:35
поделиться

5 ответов

(Отредактировано из этого сообщения на fshub )

В первый раз, когда я пошел, чтобы достичь паузы / продолжения в OCaml / F #, это бросил меня в (бесконечный) цикл, так сказать, потому что такой вещи не существует! В OCaml можно использовать исключения для выхода из цикла, потому что они очень дешевы, но в F # (в .NET) накладные расходы довольно высоки и бесполезны для «нормального» управления потоком.

Это возникло, когда некоторое время назад играли с алгоритмами сортировки (чтобы убить время), которые интенсивно используют функции repeat / until и break. Меня поразило, что функции рекурсивного хвостового вызова могут достигать точно такого же результата, лишь с небольшим ухудшением читабельности. Итак, я выбросил «изменяемый bDone» и «пока не bDone» и попытался написать код без этих императивных конструкций. Ниже приведены только части цикла и показано, как писать код в стиле repeat / until, do / while, while / do, break / continue и test-in-the-middle с использованием хвостовых вызовов. Кажется, что все они работают с точно такой же скоростью, что и традиционный оператор while в F #, но ваш пробег может отличаться (некоторые платформы могут не реализовывать хвостовой вызов должным образом и, следовательно, могут вызывать сбой, пока они не будут исправлены). В конце приводится (плохой) алгоритм сортировки, написанный в обоих стилях для сравнения.

Давайте начнем с цикла «do / while», написанного в традиционном императивном стиле F #, а затем рассмотрим функциональные варианты, которые обеспечивают одинаковый тип of цикла, а также различные семантики, такие как while / do, repeat / until, test в середине, и даже break / continue (без монад .. ммм, рабочие процессы!)

#light
(* something to work on... *)
let v = ref 0
let f() = incr v;
let g() = !v;
let N = 100000000

let imperDoWhile() =
    let mutable x = 0
    let mutable bDone = false
    while not bDone do
        f()
        x <- x + 1
        if x >= N then bDone <- true

Хорошо, это достаточно просто. Имейте в виду, что f () всегда вызывается хотя бы один раз (do / while).

Это тот же код, но в функциональном стиле. Обратите внимание, что здесь нам не нужно объявлять изменяемый.

let funDoWhile() =
    let rec loop x =
        f()             (*Do*)
        if x < N then   (*While*)
            loop (x+1)
    loop 0

Мы можем преобразовать это в традиционный do / while, поместив вызов функции внутри блока if.

let funWhileDo() =
    let rec loop x =
        if x < N then   (*While*)
            f()         (*Do*)
            loop (x+1)
    loop 0

Как насчет повторения блока до тех пор, пока какое-то условие не станет истинным ( повтор / до)? Достаточно просто ...

let funRepeatUntil() =
    let rec loop x =
        f()                 (*Repeat*)
        if x >= N then ()   (*Until*)
        else loop (x+1)
    loop 0

Что это было насчет разрыва без монад? Что ж, просто введите условное выражение, возвращающее «единицу», например:

let funBreak() =
    let rec loop() =
        let x = g()
        if x > N then ()    (*break*)
        else
            f()
            loop()
    loop()

Как насчет продолжения? Что ж, это просто еще один вызов цикла! Во-первых, с помощью синтаксического костыля:

let funBreakContinue() =
    let break' () = ()
    let rec continue' () =
        let x = g()
        if   x > N then break'()
        elif x % 2 = 0 then
            f(); f(); f();
            continue'()
        else
            f()
            continue'()
    continue'()

А затем снова без (уродливого) синтаксического костыля:

let funBreakContinue'() =
    let rec loop () =
        let x = g()
        if   x > N then ()
        elif x % 2 = 0 then
            f(); f(); f();
            loop()
        else
            f()
            loop ()
    loop()

Легко, как пирог!

Одним из хороших результатов этих форм цикла является то, что он упрощает обнаружение и реализацию состояний в ваши петли. Например, пузырьковая сортировка постоянно проходит по всему массиву, замена значений, которые неуместны, когда он их находит. Он отслеживает, произвел ли проход по массиву какие-либо обмены. В противном случае каждое значение должно быть в нужном месте, чтобы сортировка могла завершиться. В качестве оптимизации при каждом проходе через массив последнее значение в массиве оказывается отсортированным в нужное место. Таким образом, цикл может быть сокращен на единицу каждый раз. Большинство алгоритмов проверяют наличие свопа и обновляют флаг «bModified» каждый раз, когда он есть. Однако после того, как первый обмен выполнен, в этом назначении нет необходимости; оно уже установлено в true!

Вот код F #, который реализует пузырьковую сортировку (да, пузырьковая сортировка - ужасный алгоритм; быстрая сортировка камней). В конце - императивная реализация, не меняющая состояния; он обновляет флаг bModified для каждого обмена. Что интересно, императивное решение работает быстрее на крошечных массивах и всего на пару процентов медленнее на больших. (Тем не менее, это сделано для хорошего примера.)

let inline sort2 f i j (a:'a array) =
    let i' = a.[ i ]
    let j' = a.[ j ]
    if f i' j' > 0 then
        a.[ i ] <- j'
        a.[ j ] <- i'

let bubble f (xs:'a array) =
    if xs.Length = 0
    then ()

    let rec modified i endix =
        if i = endix then
            unmodified 0 (endix-1)
        else
            let j = i+1
            sort2 f i j xs
            modified j endix
    and unmodified i endix =
        if i = endix then
            ()
        else
            let j = i+1
            let i' = xs.[ i ]
            let j' = xs.[ j ]
            if f i' j' > 0 then
                xs.[ i ] <- j'
                xs.[ j ] <- i'
                modified j endix
            else
                unmodified j endix
    in unmodified 0 (xs.Length-1)

let bubble_imperitive f (xs:'a array) =
    let mutable bModified = true
    let mutable endix = xs.Length - 1
    while bModified do
        bModified <- false
        endix <- endix - 1
        for i in 0..endix do
            let j = i+1
            let i' = xs.[ i ]
            let j' = xs.[ j ]
            if f i' j' > 0 then
                xs.[ i ] <- j'
                xs.[ j ] <- i'
                bModified <- true
        done
    done
9
ответ дан 30 November 2019 в 22:24
поделиться

Старый домашний вопрос:

Функция

(define f-imperative (y) (x) ; x is a local variable
  (begin
    (set x e)
    (while (p x y)
       (set x (g x y)))
    (h x y)))

выполнена в типичном императивном стиле с присваиванием и циклом. Напишите эквивалентную функцию f-function, которая не использует императивные функции begin (последовательность), while (goto) и set (присваивание). Вы можете использовать столько «вспомогательных функций», сколько захотите, если они определены с помощью let или letrec, а не на верхнем уровне.

Одно решение:

; The idea is simple: 
; Use parameter passing for binding the values 
; of the variables and recursion instead of iteration. 
;
; For those who like theory this is the main argument for proving 
; that recursive functions (LISP, lambda calculus) have the same 
; computational power as any imperative programming language. 

(define f-functional (y) 
  (letrec (
     (f-helper (lambda (x y)
                  (if (p x y) 
                     (f-helper (g x y) y)
                     (h x y)))))
     (f-helper e y)))

; Notice that y in f-helper is invariant.  Therefore, we can rewrite
; f-helper without y as follows.

(define f-functional (y) 
  (letrec (
     (f-helper (lambda (x)
                  (if (p x y) 
                     (f-helper (g x y))
                     (h x y)))))
     (f-helper e)))

; This is not the only solution, though I think it is one of the 
; nicer ones.
2
ответ дан 30 November 2019 в 22:24
поделиться

О, теперь это отличный вопрос. Вот некоторые, фрагменты кода в python или что-то в этом роде:

  • циклы for можно заменить итераторами

    stripped_list = [line.strip () для строки в line_list]

  • циклы for могут заменить на "применить" или "карта" или "фильтр"

    карта (верхний, ['предложение', 'фрагмент']) ['SENTENCE', 'FRAGMENT']

  • вложенные циклы с композицией функций

  • хвостовая рекурсия вместо циклов

  • выражения генератора вместо циклов

    sum (x * x for x in range (10))

4
ответ дан 30 November 2019 в 22:24
поделиться

Сворачивание - очень интересная функция, которая является центральной во многих функциональных алгоритмах. Допустим, мы хотим добавить все элементы списка. В процедурном коде вы обычно создаете аккумуляторную переменную и устанавливаете ее в 0, затем перебираете список и увеличиваете аккумулятор на элемент.

В Ocaml вы выполняете то же действие функциональным образом, используя fold:

List.fold_left (+) 0 [1; 2; 3];;
- : int = 6

Используя свертку, вы можете, например, подсчитать количество слов в списке и объединить их одновременно:

List.fold_left (fun (count, concat) e -> (count + 1, concat ^ e)) (0, "") ["a"; "b"; "c"];;
- : int * string = (3, "abc")

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

module Int = struct type t = int let compare = compare end;;
module IntSet = Set.Make(Int);;

let s = List.fold_left (fun set e -> IntSet.add e set) IntSet.empty [1; 2; 3];;
val s : IntSet.t = <abstr>

IntSet.elements s;;
- : IntSet.elt list = [1; 2; 3]

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

Рекурсивно реализуйте свертку один раз, чтобы знать, как это делается под капотом, а затем используйте встроенную версию везде. Даже в C ++ с std :: accumulate !

2
ответ дан 30 November 2019 в 22:24
поделиться

Проект PLEAC почти точно преследует эту цель - реализовать все примеры в кулинарной книге perl на других языках. Вот ссылки на версию ocaml (которая является одной из трех полностью завершенных) http://pleac.sourceforge.net/pleac_ocaml/index.html

2
ответ дан 30 November 2019 в 22:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: