查找F#中字符串序列中字符串的所有出现次数?

dde*_*dde 2 string f#

C#和VB.NET中的字符串处理对我来说很容易,但是理解如何在F#中做同样的事情并不那么容易.我正在读两本Apress F#书(基础和专家).大多数样本都是数字运算,而且我认为,字符串操作很少.特别是,seq {sequence-expression}Lists的样本.

我有一个C#程序,我想转换为F#.这是它的作用:

  1. 打开一个txt文件
  2. 拆分文件段落,查找段落之间的CRLF
  3. 拆分段落线,寻找.!?线之间
  4. 分割线词,寻找词之间的空格
  5. 输出段落,行和单词的数量
  6. 循环收集单词,查找并计算集合中字符串的所有出现次数,标记找到的单词的位置.

这是一个简单的例子,说明我在C#中可以做什么,但在F#中还没有.

假设这是一个文本文件:

命令,最高法院,纽约县(Paul G Someone),于2008年3月18日进入,该行为因旅行中遭受的人身伤害而摔倒,据称由于被告城市或联合麦克弗森的疏忽造成了坑洼,联合麦克弗森对其承包商(Mallen)提起的第三方诉讼,就其上诉而言,否认,不合时宜,Mallen的简易判决动议驳回了投诉和第三方投诉,一致肯定,没有任何费用.

通过法院,规定或其他方式,各方可以自由地规划其程序性过程.因此,我们肯定否认Mallen的动议是不合时宜的,因为Mallen没有为迟到的申请提供借口.

我得到这个输出:

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
Run Code Online (Sandbox Code Playgroud)

行应该被称为句子:(

有几个令牌.我会说超过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;
    }
Run Code Online (Sandbox Code Playgroud)

实际上,它是一个非常简单的Windows应用程序.包含一个RichTextBox和三个ListBox(已找到段落,行,标记)的表单,用于显示输出的标签和一个按钮.

dah*_*byk 5

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)
Run Code Online (Sandbox Code Playgroud)

首先,让我们看看段落.我喜欢Brian的方法来计算分隔段落的空行.所以我们过滤只查找空行,计算它们,然后根据该值返回我们的段落计数:

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

对于句子,我们可以将全文视为一系列字符并计算句子结尾字符的数量.因为它是F#,让我们使用模式匹配:

let numSentences =
    let isSentenceEndChar c = match c with
                              | '.' | '!' | '?' -> true
                              | _ -> false
    text |> Seq.filter isSentenceEndChar
         |> Seq.length
Run Code Online (Sandbox Code Playgroud)

匹配单词可以像简单的正则表达式一样简单,但可能会随着你想要处理标点符号的方式而变化:

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

numParagraphs |> printfn "%d paragraphs" 
numSentences  |> printfn "%d sentences"
numWords      |> printfn "%d words"
Run Code Online (Sandbox Code Playgroud)

最后,我们定义了一个打印令牌出现的函数,可以很容易地应用于令牌列表:

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
Run Code Online (Sandbox Code Playgroud)

请注意,由于其尾随逗号,此代码找不到"因此".您可能希望调整words生成方式或tokenMatch定义方式.