Обработка списка с промежуточным состоянием - PullRequest
2 голосов
/ 22 сентября 2011

Я обрабатываю список строк, вы можете думать о них как о строках книги. Когда строка пуста, она должна быть отброшена. Когда это заголовок, он «сохраняется» как текущий заголовок. Каждая «нормальная» строка должна генерировать объект со своим текстом и текущим заголовком. В конце у вас есть список строк, каждая с соответствующим заголовком.

Ex:.

- Chapter 1

Lorem ipsum dolor sit amet
consectetur adipisicing elit

- Chapter 2

sed do eiusmod tempor
incididunt u

Первая строка - это заголовок, вторая строка должна быть отброшена, затем две строки сохраняются как абзацы, каждая с заголовком "Глава 1" И так далее. В итоге вы получите коллекцию, похожую на:

{"Lorem ipsum...", "Chapter 1"},
{"consectetur...", "Chapter 1"},
{"sed do...", "Chapter 2"},
{"incididunt ...", "Chater 2"}

Я знаю, что модель заголовка / абзаца не имеет смысла на 100%, но я упростил модель для иллюстрации проблемы.

Это моё итеративное решение:

let parseText allLines =
    let mutable currentTitle = String.Empty
    seq {
        for line in allLines do
            match parseLine line with
            | Empty -> 0 |> ignore
            | Title caption ->
                currentTitle <- caption
            | Body text ->
                    yield new Paragraph(currentTitle, text)
    }

Первая проблема - я должен отбрасывать пустые строки, я делаю это с 0 |> ignore, но для меня это выглядит довольно плохо. Как правильно это делать (без предварительной фильтрации списка)?

Хвосто-рекурсивная версия этой функции проста:

let rec parseText allLines currentTitle paragraphs =
    match allLines with
    | [] -> paragraphs
    | head :: tail -> 
        match head with
        | Empty -> parseText tail currentTitle paragraphs
        | Title caption -> parseText tail caption paragraphs
        | Body text -> parseText tail currentTitle (new Paragraph(currentTitle, text) :: tail)

Вопрос (ы):

  • Есть ли существенная разница между двумя версиями (Стиль / производительность / и т.д.)?
  • Есть ли лучший подход для решения этой проблемы проблема? Можно ли это сделать с помощью одного List.map?

Ответы [ 3 ]

2 голосов
/ 24 сентября 2011

Хотя нет ни одного List.Map, вот решение, которое я придумал:

let parseText allLines = 
    allLines 
    |> Seq.fold (fun (currentTitle,paragraphs) line -> 
        match parseLine line with
        | Empty -> currentTitle,paragraphs
        | Title caption -> caption,paragraphs
        | Body text -> String.Empty,Paragraph(currentTitle, text)::paragraphs
        ) (String.Empty,[])
    |> snd

Я использую фолд с (currentTitle,paragraphs) в качестве состояния. snd используется для извлечения результата (это s eco nd часть кортежа состояния).

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

Кстати, ваш код последовательности компилируется? Мне пришлось заменить mutable currentTitle = String.Empty на currentTitle = ref String.Empty.

2 голосов
/ 22 сентября 2011

Вы можете заменить 0 |> ignore на () (единица измерения), что не допускается.Самое большое различие между вашими двумя реализациями в том, что первая - это ленивая, что может быть полезно для большого ввода.

Следующее также может работать для вас (это самое простое решение, которое я могу придумать):

let parseText (lines:seq<string>) =
  lines
  |> Seq.filter (fun line -> line.Trim().Length > 0)
  |> Seq.pairwise (fun (title, body) -> Paragraph(title, body))

Если нет, возможно, это будет работать:

let parseText (lines:seq<string>) =
  lines
  |> Seq.choose (fun line -> 
    match line.Trim() with
    | "" | null -> None
    | Title title -> Some title
    | Body text -> Some text)
  |> Seq.pairwise (fun (title, body) -> Paragraph(title, body))
1 голос
/ 22 сентября 2011

Ниже приведена одна из таких реализаций (хотя и не проверенная, но я надеюсь, что она даст вам представление)

let isNotEmpty l = match l with
                   | Empty -> false
                   | _ -> true

let parseText allLines =
    allLines |> Seq.map parseLine |> Seq.filter isNotEmpty
    |> Seq.scan (fun (c,t,b) i -> match i with
                                  | Title tl -> (0,tl,"")
                                  | Body bb -> (1,t,bb) 
                                  | _ -> (0,t,b)) (0,"","")
    |> Seq.filter (fun (c,_,_) -> c > 0)
    |> Seq.map (fun (_,t,b) -> Paragraph(t,b) )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...