2016-07-06 2 views
5

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

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

  1. добавить данные в передней
  2. Вытащите последний элемент (можно просто выбросить)

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

Я использую F #, но любой язык приветствуется.

+3

Возможно отношение: [ 'Data.Sequence'] (http://hackage.haskell.org/package /containers-0.5.7.1/docs/Data-Sequence.html) – chi

+0

@chi Как это работает? Я предполагаю, что я плохо сформулировал эту последнюю часть, в которой я хотел бы использовать эту вещь в F #, но у меня нет большой идеи о том, как фактически определяется Data.Sequence. Похоже, что операции довольно близки к тому, что я хочу. – Jwosty

+1

@Jwosty Если вы хотите посмотреть на реализацию, вы можете щелкнуть по кнопке «source» на стороне, и она приведет вас к ней: http://hackage.haskell.org/package/containers-0.5.7.1/ docs/src/Data.Sequence.html – jkeuhlen

ответ

11

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

Если вы используете F # и .NET есть, например:

Если вы хотите прочитать о том, как реализовать функциональную очередь я рекомендую Purely Functional Data Structures Крисом Okasaki.

Один из первых способов, которым Окасаки реализует функциональную очередь, состоит в использовании двух List<>, по одному из которых вы нажимаете и нажимаете. Когда список постов пуст, то очередь push меняется на противоположную и становится списком pop.

Имейте в виду, что это во многом довольно неэффективный очереди, но это также довольно просто:

type Queue<'T> = 'T list*'T list 

let empty<'T> : Queue<'T> = [], [] 

let isEmpty ((f, r) : Queue<'T>) : bool = 
    match f, r with 
    | [] , [] -> true 
    | _  , _ -> false 

let headAndTail ((f, r) : Queue<'T>) : 'T*Queue<'T> = 
    match f, r with 
    | [] , [] -> failwith "Queue is empty" 
    | v::vs , r -> v, (vs, r) 
    | _  , r -> let v::vs = List.rev r in v, (vs, []) 

let snoc ((f, r) : Queue<'T>) (v : 'T) : Queue<'T> = (f, v::r) 

let fold (f : 'S -> 'T -> 'S) (s : 'S) (q : Queue<'T>) : 'S = 
    let rec loop ss qq = 
    if isEmpty qq then ss 
    else 
     let hh, tt = headAndTail qq 
     loop (f ss hh) tt 
    loop s q 

let ofArray (vs : 'T []) : Queue<'T> = vs |> Array.fold snoc empty 

[<EntryPoint>] 
let main argv = 
    let q = [| 1..20 |] |> ofArray 
    fold (fun _ v -> printfn "%A" v)() q 
    0 
+4

Я бы не сказал, что это неэффективно. Все операции амортизируются O (1), как показано в книге, которую вы рекомендуете. Существует случайная дорогостоящая операция «обратного», но они оказались достаточно редкими, что ваше среднее время низкое. – amalloy

+0

Мне это нравится. Благодаря! – Jwosty

+1

Окасаки Окасаки поддерживает _invariance_, что передний список может быть пустым, только если задний список также пуст и использует эту инвариантность в своем анализе амортизации. (см. функцию 'fill' в [здесь] (http://stackoverflow.com/a/37505858/625914)). Вы нарушаете эту инвариантность. –

Смежные вопросы