C# 和 VB.NET 中的字符串处理对我来说很容易,但了解如何在 F# 中执行相同的操作就不那么容易了。我正在阅读两本 Apress F# 书籍(《基础》和《专家》)。大多数样本都是数字运算,我认为很少有字符串操作。特别是 seq {equence-expression } 和 Lists 的示例。
我有一个 C# 程序想要转换为 F#。它的作用如下:
- 打开txt文件
- 分割文件段落,在段落之间查找 CRLF
- 分割段落行,查找 . ! ?行与行之间
- 分割行单词,寻找单词之间的“”空格
- 输出段落数、行数和单词数
- 循环单词集合,查找并统计集合中出现的所有字符串,标记找到的单词的位置。
下面是一个简单的示例,展示了我可以在 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/