string - 在 F# 中查找字符串序列中所有出现的字符串?

标签 string f#

C# 和 VB.NET 中的字符串处理对我来说很容易,但了解如何在 F# 中执行相同的操作就不那么容易了。我正在阅读两本 Apress F# 书籍(《基础》和《专家》)。大多数样本都是数字运算,我认为很少有字符串操作。特别是 seq {equence-expression }Lists 的示例。

我有一个 C# 程序想要转换为 F#。它的作用如下:

  1. 打开txt文件
  2. 分割文件段落,在段落之间查找 CRLF
  3. 分割段落行,查找 . ! ?行与行之间
  4. 分割行单词,寻找单词之间的“”空格
  5. 输出段落数、行数和单词数
  6. 循环单词集合,查找并统计集合中出现的所有字符串,标记找到的单词的位置。

下面是一个简单的示例,展示了我可以在 C# 中执行但在 F# 中无法执行的操作。

假设这是一个文本文件:

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.

我得到这个输出:

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

行应该被称为句子:(

有几个 token 。我想说按类(class)分组超过 100 个。我必须多次迭代相同的文本来尝试匹配每个标记。这是部分代码。它展示了我如何拆分句子,将它们放入列表框中,这有助于轻松获取项目计数。这适用于段落、句子和标记。它还显示了我如何依赖 for 和 foreach。我想通过使用如果可能的话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(找到的段落、行、标记)、用于显示输出的标签和一个按钮的表单。

最佳答案

Brian 有一个良好的开端,但函数式代码将更多地关注您要尝试做的“什么”,而不是“如何做”。

我们可以以类似的方式开始:

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"

最后,我们定义一个函数来打印 token 出现情况,该函数可以轻松应用于 token 列表:

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

请注意,此代码没有找到“thus”,因为它的尾随逗号。您可能需要调整 words 的生成方式或 tokenMatch 的定义方式。

关于string - 在 F# 中查找字符串序列中所有出现的字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1114203/

相关文章:

visual-studio - F# 程序集中的默认程序集修订号始终为 0

javascript - 如何获取字符串中两位数字的值

java - Java中如何从一个字符串中获取多个变量集

F#递归函数退出?

performance - 良好的F#性能分析工具

F# NativePtr.stackalloc 意外堆栈溢出

swift - 在 F# 中继承多个类

java - 如何为一个巨大的字符串列表计算一个好的哈希码?

c++ - 节省内存的 C++ 字符串(interning、ropes、copy-on-write 等)

c - 尝试从 char*[] 复制所有字符串但出现段错误