Найти все вхождения строки в последовательности строк в F #? - PullRequest
2 голосов
/ 11 июля 2009

Обработка строк в C # и VB.NET для меня проста, но понять, как сделать то же самое в F #, не так просто. Я читаю две книги Apress F # (Основы и Эксперт). Большинство образцов хрустят числами, и я думаю, что очень мало манипуляций со строками. В частности, образцы seq {sequence-expression} и Lists .

У меня есть программа на C #, которую я хочу преобразовать в F #. Вот что он делает:

  1. Открыть текстовый файл
  2. разделить файл абзацев, ищите CRLF между абзацами
  3. разделите строки абзаца, ищите. ! ? между строк
  4. слова, разделенные строкой, ищите пробел между словами "1016 *
  5. выводить количество абзацев, строк и слов
  6. Зацикливание набора слов, поиск и подсчет всех вхождений строки в коллекции, отметка местоположений найденного слова.

Вот простой пример того, что я могу сделать в C #, но еще не в F #.

Предположим, это текстовый файл:

Приказ Верховного суда округа Нью-Йорк (Paul G Someone), поступил 18 марта, 2008, который в акции для личного травмы, полученные в поездке и падении над выбоиной, предположительно созданной халатность подсудимых города или Консолидированный МакФерсон и сторонний действия Консолидедед Макферсон против его подрядчик (Маллен), поскольку обжаловано, отклонено, как несвоевременно, Ходатайство Маллена о суммарном суждении отклонение жалобы и сторонняя жалоба, единогласно подтверждено, без затрат.

Сторонам предоставляется большая свобода в наметить их процедурный курс через суды, по оговорке или иначе. Таким образом, мы подтверждаем отрицание движения Маллена как несвоевременного с Маллен не предложил никаких оправданий покойному подачи.

Я получаю этот вывод:

2 Paragraphs
3 Lines
109 Words

Found Tokens: 2
Token insofar: ocurrence(s) 1: position(s): 52
Token thus: ocurrence(s) 1: position(s): 91

Строки должны были называться предложениями: (

Есть несколько токенов. Я бы сказал, что более 100 сгруппированы по классам. Мне приходится перебирать один и тот же текст несколько раз, пытаясь сопоставить каждый токен. Вот части кода. Он показывает, как я делю предложения, помещаю их в ListBox, что помогает легко получить количество элементов. Это работает для абзацев, предложений и токенов. И это также показывает, как я полагаюсь на и для каждого. Именно такого подхода я хочу избежать, используя, если это возможно, seq {sequence-expression} и Lists и seq.iter или List.iter и все, что соответствует токену ... с этим необходимо.

    /// <summary>
    /// split the text into sentences and displays
    /// the results in a list box
    /// </summary>
    private void btnParseText_Click(object sender, EventArgs e)
    {
        lstLines.Items.Clear();

        ArrayList al = SplitLines(richTextBoxParagraphs.Text);
        for (int i = 0; i < al.Count; i++)
            //populate a list box
            lstLines.Items.Add(al[i].ToString());
    }


    /// <summary>
    /// parse a body of text into sentences 
    /// </summary>
    private ArrayList SplitLines(string sText)
    {

        // array list tto hold the sentences
        ArrayList al = new ArrayList();

        // split the lines regexp
        string[] splitLines = 
            Regex.Split(sText, @"(?<=['""A-Za-z0-9][\.\!\?])\s+(?=[A-Z])");

        // loop the sentences
        for (int i = 0; i < splitLines.Length; i++)
        {
            string sOneLine =
                splitLines[i].Replace(Environment.NewLine, string.Empty);
            al.Add(sOneLine.Trim());
        }

        // update statistics
        lblLineCount.Text = "Line Count: " + 
            GetLineCount(splitLines).ToString();
        // words
        lblWordCount.Text = "Word Count: " + 
            GetWordCount(al).ToString();
        // tokens
        lblTokenCount.Text = "Token Count: " +
            GetTokenCount(al).ToString();

        // return the arraylist
        return al;
    }

    /// <summary>
    /// count of all words contained in the ArrayList 
    /// </summary>
    public int GetWordCount(ArrayList allLines)
    {
        // return value
        int rtn = 0;

        // iterate through list
        foreach (string sLine in allLines)
        {
            // empty space is the split char
            char[] arrSplitChars = {' '};

            // create a string array and populate
            string[] arrWords = sSentence.Split(arrSplitChars, StringSplitOptions.RemoveEmptyEntries);
            rtn += arrWords.Length;
        }

        // return word count
        return rtn;
    }

На самом деле это очень простое приложение для Windows. Форма с одним RichTextBox и тремя ListBox (абзацы, строки, найденные токены), метками для отображения вывода и одной кнопкой.

Ответы [ 3 ]

5 голосов
/ 12 июля 2009

У Брайана хорошее начало, но функциональный код будет сосредоточен больше на том «что» вы пытаетесь сделать, чем «как».

Мы можем начать аналогичным образом:

open System
open System.Text.RegularExpressions 

let text = @"Order, Supreme Court, New York County (Paul G Someone), entered..."

let lines = text.Split([|Environment.NewLine|], StringSplitOptions.None)

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

let numParagraphs = 
    let blankLines = lines |> Seq.filter (fun line -> Regex.IsMatch(line, @"^\s*$"))
                           |> Seq.length
    blankLines + 1

Для предложений мы можем просматривать полный текст в виде последовательности символов и подсчитывать количество символов в конце предложения. Поскольку это F #, давайте использовать сопоставление с образцом:

let numSentences =
    let isSentenceEndChar c = match c with
                              | '.' | '!' | '?' -> true
                              | _ -> false
    text |> Seq.filter isSentenceEndChar
         |> Seq.length

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

let words = Regex.Split(text, "\s+")
let numWords = words.Length

numParagraphs |> printfn "%d paragraphs" 
numSentences  |> printfn "%d sentences"
numWords      |> printfn "%d words"

Наконец, мы определяем функцию для печати токенов, которая легко применяется к списку токенов:

let findToken token =
    let tokenMatch (word : string) = word.Equals(token, StringComparison.OrdinalIgnoreCase)
    words |> Seq.iteri (fun n word ->
        if tokenMatch word then
            printfn "Found %s at word %d" word n
    )

let tokensToFind = ["insofar"; "thus"; "the"]
tokensToFind |> Seq.iter findToken

Обратите внимание, что этот код не находит "таким образом" из-за запятой. Скорее всего, вы захотите настроить способ генерации words или tokenMatch.

1 голос
/ 11 июля 2009

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

Здесь не обязательно много специфичного для F #, вы можете сделать это почти одинаково на любом .Net языке. Существует несколько стратегий, которые вы можете использовать, например, ниже, я использую регулярные выражения для выделения слов ... хотя ниже приведена только пара идиом F #.

open System
open System.Text.RegularExpressions 

let text = @"Order, Supreme Court, New York County (Paul G Someone), entered 
March 18, 2008, which, in an action for personal injuries sustained in a 
trip and fall over a pothole allegedly created by the negligence of 
defendants City or Consolidated McPherson, and a third-party action by 
Consolidated McPherson against its contractor (Mallen), insofar as appealed 
from, denied, as untimely, Mallen's motion for summary judgment dismissing 
the complaint and third-party complaint, unanimously affirmed, without costs.

Parties are afforded great latitude in charting their procedural course 
through the courts, by stipulation or otherwise. Thus, we affirm the denial 
of Mallen's motion as untimely since Mallen offered no excuse for the late 
filing."

let lines = text.Split([|'\n'|])
// If was in file, could use
//let lines = System.IO.File.ReadAllLines(@"c:\path\filename.txt")
// just like C#.  For this example, assume have giant string above

let fullText = String.Join(" ", lines)
let numParagraphs = 
    let mutable count = 1
    for line in lines do
        // look for blank lines, assume each delimits another paragraph
        if Regex.IsMatch(line, @"^\s*$") then
            count <- count + 1
    count
let numSentences =     
    let mutable count = 1
    for c in fullText do
        if c = '.' || c = '!' || c = '?' then
            count <- count + 1
    count
let words =
    let wordRegex = new Regex(@"\b(\w+)\b")
    let fullText = String.Join(" ", lines)
    [| for m in wordRegex.Matches(fullText) do
        yield m.Value |]
printfn "%d paragraphs" numParagraphs
printfn "%d sentences" numSentences
printfn "%d words" words.Length
let Find token =
    words |> Seq.iteri (fun n word ->
        if 0=String.Compare(word, token, 
                            StringComparison.OrdinalIgnoreCase) then
            printfn "Found %s at word %d" word n
    )
let tokensToFind = ["insofar"; "thus"; "the"]
for token in tokensToFind do
    Find token
0 голосов
/ 11 июля 2009

Не могли бы вы опубликовать свою C # -программу? (Отредактируйте свой вопрос)

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

Если вы использовали String.Split в C #: это одно и то же:

open System
let results = "Hello World".Split [|' '|]
let results2 = "Hello, World".Split ([| ", "|], StringSplitOptions.None)

Чтобы объединить полученные последовательности, вы можете объединить yield и yield!.

Абстрактный пример

let list = [ yield! [1..8]; for i in 3..10 do yield i * i ]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...