我试图从列表中创建一个字符串。但是,我希望它以一种有意义的方式排列,所以不是字符串是#34;我有苹果,香蕉,爸爸的柑橘。",我希望它是" 34;我有一个苹果,一个香蕉和爸爸的柑橘。"。 (Strings是" apple"," banana"和" Papa'柑橘"。)
我宁愿不改变构成我名单的字符串;我正在处理的是一个mod,它会根据启用的其他mod修改列表,因此添加'和'每组的最后一个字符串都不能很好地工作。
总而言之,我想要的代码会将列表转换为字符串,添加' a'在以辅音开头的单词面前,' an'在带有元音的单词面前,也没有在带有撇号的单词前面。
谢谢!
答案 0 :(得分:1)
目前尚不清楚您的输入是什么样的,但它可能类似于此控制台应用程序。但是,英语不是我的第一语言,所以我可能会非常错误:
static void Main(string[] args)
{
List<string> strings = new List<string>
{
"apple",
"banana",
"Papa's citrus"
};
var lastNdx = strings.Count - 1;
var sentence = "I have " + String.Join(", ",
strings.Select((s, ndx) =>
{
var ls = s.ToLower();
string ret = "";
if ("aeiou".Contains(ls[0]))
ret = "an " + s;
else if (ls.Contains("\'"))
ret = s;
else ret = "a " + s;
if (ndx == lastNdx)
ret = "and " + ret;
return ret;
}).ToArray() );
Console.WriteLine(sentence);
}
答案 1 :(得分:1)
使用一些方便的扩展方法:
public static class IEnumerableExt {
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop);
public static IEnumerable<T> Drop<T>(this ICollection<T> src, int drop) => (drop < 0) ? src.Leave(-drop) : src.Skip(drop);
}
public static class StringExt {
public static string UpTo(this string s, Regex stopRE) {
var m = stopRE.Match(s);
if (m.Success)
return s.Substring(0, m.Index);
else
return s;
}
public static string Join(this IEnumerable<string> strings, string sep) => String.Join(sep, strings.ToArray());
public static bool EndsWithOneOf(this string s, params string[] endings) => endings.Any(e => s.EndsWith(e));
}
然后,您可以创建一些自定义扩展方法来解决问题:
public static class FruitExt {
public static readonly HashSet<char> consonants = "bcdfghjklmnpqrstvwxyz".ToHashSet();
public static bool BeginsWithConsonant(this string w) => consonants.Contains(w[0]);
public static bool IsPossessive(this string w) => w.UpTo(new Regex(@"\s")).EndsWithOneOf("'s", "'");
public static string WithIndefiniteArticle(this string w) => (w.BeginsWithConsonant() ? "a " : "an ") + w;
public static string ArticleOrPossessive(this string w) => w.IsPossessive() ? w : w.WithIndefiniteArticle();
}
请注意BeginsWithConsonant
对于以Y开头作为元音的那些(四个?)单词失败 - 如果它困扰你,你可以为方法添加例外。
现在答案很简单LINQ:
var ans = ("I have "+src.Drop(-1).Select(w => w.ArticleOrPossessive()).Join(", ")+" and "+src.Last().ArticleOrPossessive()+".");
在一般情况下, Drop
和Last
并不是特别有效,但由于我们知道我们使用的是List
,因此它们很好(并且数据将会无论如何都要短暂。
答案 2 :(得分:1)
这个答案适用于@Stilgar。这有点复杂,所以我做了第二个答案。
下载最新版本的CMU Pronunciation Dictionary files并将其存储在一个文件夹中。在CMUDictExt
类中设置路径以使用该文件夹:
public static class CMUDictExt {
const string cmuFolder = @"D:\";
static IEnumerable<string> CMUFiles = Directory.EnumerateFiles(cmuFolder, "cmudict-*");
static Regex cmudictName = new Regex(@"cmudict-(?:\d+(?:\.\d+)?[a-z]?)+\.?(.*)$", RegexOptions.Compiled);
static string CMUFile(string ext) => CMUFiles.First(f => cmudictName.Match(f).Groups[1].Value == ext);
static Dictionary<string, string> phones;
static Dictionary<string, string[]> pronunciations;
public static ILookup<string, string> SymbolWords;
static HashSet<string> exceptions;
static CMUDictExt() {
phones = File.ReadLines(CMUFile("phones"))
.Select(l => l.Split('\t'))
.ToDictionary(pa => pa[0], pa => pa[1]);
pronunciations = File.ReadLines(CMUFile(""))
.Where(l => !l.StartsWith(";;;"))
.Where(l => Char.IsLetter(l[0]))
.Select(l => l.Split(" ").ToArray())
.ToDictionary(wg => wg[0].ToLowerInvariant(), wg => wg[1].Split(' '));
SymbolWords = pronunciations.SelectMany(wp => wp.Value.Select(s => (Word: wp.Key, s)))
.ToLookup(wp => wp.s, wp => wp.Word);
exceptions = pronunciations.Where(wp => (wp.Key.StartsWithVowel() ^ wp.Value[0].Phone() == "vowel")).Select(wp => wp.Key).ToHashSet();
}
public static string Phone(this string aSymbol) => phones.GetValueOrDefault(aSymbol.UpTo(ch => Char.IsDigit(ch)), String.Empty);
static string[] emptyStringArray = new string[] {};
public static string[] Pronunciation(this string aWord) => pronunciations.GetValueOrDefault(aWord.ToLowerInvariant(), emptyStringArray);
public static bool HasPronunciation(this string aWord) => pronunciations.GetValueOrDefault(aWord.ToLowerInvariant(), null) != null;
static readonly HashSet<char> vowels = "aeiou".ToHashSet<char>();
public static bool StartsWithVowel(this string w) => vowels.Contains(w[0]);
public static bool BeginsWithVowelSound(this string aWord) => exceptions.Contains(aWord) ? !aWord.StartsWithVowel() : aWord.StartsWithVowel(); // guess if not found
}
使用与以前类似的扩展方法:
public static class IEnumerableExt {
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source) => new HashSet<T>(source);
public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop);
public static IEnumerable<T> Drop<T>(this ICollection<T> src, int drop) => (drop < 0) ? src.Leave(-drop) : src.Skip(drop);
public static T MinBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector, Comparer<TKey> keyComparer) => src.Aggregate((a, b) => keyComparer.Compare(keySelector(a), keySelector(b)) < 0 ? a : b);
public static T MinBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keySelector) => src.Aggregate((a, b) => Comparer<TKey>.Default.Compare(keySelector(a), keySelector(b)) < 0 ? a : b);
}
public static class StringExt {
public static string UpTo(this string s, Regex stopRE) {
var m = stopRE.Match(s);
if (m.Success)
return s.Substring(0, m.Index);
else
return s;
}
public static string UpTo(this string s, Func<char, bool> testfn) {
var m = s.Select((ch, Index) => new { ch, Index, Success = testfn(ch) }).FirstOrDefault(cit => cit.Success);
if (m != null && m.Success)
return s.Substring(0, m.Index);
else
return s;
}
public static string Join(this IEnumerable<string> strings, string sep) => String.Join(sep, strings.ToArray());
public static bool EndsWithOneOf(this string s, params string[] endings) => endings.Any(e => s.EndsWith(e));
public static IEnumerable<string> Split(this string s, params string[] seps) => s.Split(StringSplitOptions.None, seps);
public static IEnumerable<string> Split(this string s, StringSplitOptions so, params string[] seps) {
int pos = 0;
do {
var sepPos = seps.Select(sep => new { pos = s.IndexOf(sep, pos) < 0 ? s.Length : s.IndexOf(sep, pos), len = sep.Length }).MinBy(pl => pl.pos);
if (sepPos.pos > pos || so == StringSplitOptions.None)
yield return s.Substring(pos, sepPos.pos - pos);
pos = sepPos.pos + sepPos.len;
} while (pos <= s.Length);
}
public static string FirstWord(this string phrase) => phrase.UpTo(ch => Char.IsWhiteSpace(ch));
public static bool IsAllLetters(this string s) => s.All(ch => Char.IsLetter(ch)); // faster than Regex
}
public static class DictionaryExt {
public static TV GetValueOrDefault<TK, TV>(this IDictionary<TK, TV> dict, TK key, TV defaultValue) => dict.TryGetValue(key, out TV value) ? value : defaultValue;
}
您现在可以创建一些自定义扩展来处理问题的部分内容:
public static class FruitExt {
public static bool IsPossessive(this string phrase) => phrase.FirstWord().EndsWithOneOf("'s", "'");
public static string WithIndefiniteArticle(this string phrase) => (phrase.FirstWord().BeginsWithVowelSound() ? "an " : "a ") + phrase;
public static string ArticleOrPossessive(this string phrase) => phrase.IsPossessive() ? phrase : phrase.WithIndefiniteArticle();
}
现在答案的计算方法与之前相同,但在默认检查元音之前,它会正确处理许多英语语言异常:
var ans = ("I have " + src.Drop(-1).Select(w => w.ArticleOrPossessive()).Join(", ") + " and " + src.Last().ArticleOrPossessive() + ".");
示例输出:
I have an apple, a banana, Papa's citrus, an honest judge, a highchair, a university and an understanding.