【问题标题】:How to use use appropriate expression trees to handle different scenarios如何使用合适的表达式树来处理不同的场景
【发布时间】:2013-12-10 03:03:21
【问题描述】:

我有一个场景,可以将不同的 csv 文件处理为通用格式。我的 CSV 文件包含不同学生的学生 ID、姓名和分数。但是 csv 文件中的数据分布因文件而异。例如,在第一个csv文件中,学生ID、名字、姓氏、生物、化学、物理、英语、法语、数学的百分百分数分布如下:

分布类型 1:

001,约翰·多伊,098、099、095、088、075、096

002,简,多伊,099、095、096、085、095、099

在另一个 csv 文件中,相同的数据分布为学生的名字、姓氏、学生 ID、英语和法语的 100 分、几何和代数的 50 分以及最后 100 分的分数化学、生物和物理,例如:

分布类型 2:

约翰,多伊,001、088、075、048、048、099、098、095

简,多伊,002、085、095、050、049、095、099、096

上述两种分布的输出应该是:

学生 ID [空格] 学生名字 [空格] 学生姓氏 [空格] 语言成绩百分比(即英语 + 法语)[空格] 数学成绩百分比(代数 + 几何)[空格] 百分比科学成绩(物理+化学+生物)

所以对于上述两种分布,输出将是

001 约翰·多伊 081 096 097

002 简·多伊 090 099 097

要在第一种情况下转换输入,代码如下:

    string csv1 = @"D:\MyPath\Distribution1.csv";
    string outputPath = @"D:\MyPath\OutputFile.txt";
    string line = string.Empty;
    string outputLine = string.Empty;
    string[] data = null;


    using (StreamReader sr = new StreamReader(csv1)) {

        while (!sr.EndOfStream) {
            line = sr.ReadLine();                   
            data = line.Split(new char[] {','}, StringSplitOptions.None);

            outputLine = data[0] + " "
                + data[1] + " " + data[2] + " "
                + Convert.ToString(((Convert.ToInt32(data[6]) + Convert.ToInt32(data[7])) * 100)/200) + " "
                + data[8] + " "
                + Convert.ToString(((Convert.ToInt32(data[3]) + Convert.ToInt32(data[4]) + Convert.ToInt32(data[5])) * 100) / 300);

            using (StreamWriter sw = new StreamWriter(outputPath)) {
                sw.WriteLine(outputLine);
            }
        }
    }

要处理第二个 csv 文件,它将如下:

    string csv2 = @"D:\MyPath\Distributio21.csv";
    string outputPath = @"D:\MyPath\OutputFile.txt";
    string line = string.Empty;
    string outputLine = string.Empty;
    string[] data = null;


    using (StreamReader sr = new StreamReader(csv2)) {

        while (!sr.EndOfStream) {
            line = sr.ReadLine();                   
            data = line.Split(new char[] {','}, StringSplitOptions.None);

            outputLine = data[2] + " "
                + data[0] + " " + data[1] + " "
                + Convert.ToString(((Convert.ToInt32(data[3]) + Convert.ToInt32(data[4])) * 100)/200) + " "
                + Convert.ToString(((Convert.ToInt32(data[5]) + Convert.ToInt32(data[6])) * 100)/100) + " "
                + Convert.ToString(((Convert.ToInt32(data[7]) + Convert.ToInt32(data[8]) + Convert.ToInt32(data[9])) * 100) / 300);

            using (StreamWriter sw = new StreamWriter(outputPath)) {
                sw.WriteLine(outputLine);
            }
        }
    }

生成 outputLine 的代码显然并不理想。我希望用表达式树替换它们。寻求有关如何替换以下代码 sn-ps 的输入:

    outputLine = data[2] + " "
        + data[0] + " " + data[1] + " "
        + Convert.ToString(((Convert.ToInt32(data[3]) + Convert.ToInt32(data[4])) * 100)/200) + " "
        + Convert.ToString(((Convert.ToInt32(data[5]) + Convert.ToInt32(data[6])) * 100)/100) + " "
        + Convert.ToString(((Convert.ToInt32(data[7]) + Convert.ToInt32(data[8]) + Convert.ToInt32(data[9])) * 100) / 300);

    outputLine = data[0] + " "
        + data[1] + " " + data[2] + " "
        + Convert.ToString(((Convert.ToInt32(data[6]) + Convert.ToInt32(data[7])) * 100)/200) + " "
        + data[8] + " "
        + Convert.ToString(((Convert.ToInt32(data[3]) + Convert.ToInt32(data[4]) + Convert.ToInt32(data[5])) * 100) / 300);

带有表达式树。

还请让我知道表达式树如何与相应的输入文件相关联。我希望调用与文件类型对应的适当表达式树。

对此的任何帮助都非常感谢。

【问题讨论】:

  • 为什么是表达式树?是什么让您认为它们是正确的解决方案?
  • 另外,您是否已经有办法找出文件的格式?还是你必须猜?
  • 如果表达式树以外的东西可以与每个不同的输入分布相关联,并且可以调用一个,请告诉我。是的,对于给定的输入文件名,分布是固定的。这不需要猜测。

标签: c# expression-trees


【解决方案1】:

你的代码最大的问题是没有separation of concerns。你应该做的是将你的代码分成三个部分:

  1. 读取输入文件并生成一组对象作为输出。
  2. 结合分数,输出不同对象的集合。
  3. 将组合分数写入文件。

在你的情况下,这样做可能看起来像这样:

class StudentScores1
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public int Biology { get; set; }
    public int Chemistry { get; set; }
    public int Physics { get; set; }
    public int English { get; set; }
    public int French { get; set; }
    public int Mathematics { get; set; }
}

class CombinedScores
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public int Languages { get; set; }
    public int Mathematics { get; set; }
    public int Sciences { get; set; }
}

…

static IEnumerable<StudentScores1> ParseScores1(string inputPath)
{
    using (var sr = new StreamReader(inputPath))
    {
        while (!sr.EndOfStream)
        {
            var data = sr.ReadLine().Split(',');

            yield return
                new StudentScores1
                {
                    Id = int.Parse(data[0]),
                    FirstName = data[1],
                    LastName = data[2],
                    Biology = int.Parse(data[3]),
                    Chemistry = int.Parse(data[4]),
                    Physics = int.Parse(data[5]),
                    English = int.Parse(data[6]),
                    French = int.Parse(data[7]),
                    Mathematics = int.Parse(data[8])
                };
        }
    }
}

static int Average(params int[] inputs)
{
    return inputs.Sum() / inputs.Length;
}

static IEnumerable<CombinedScores> CombineScores1(
    IEnumerable<StudentScores1> scores)
{
    return scores.Select(
        s =>
        new CombinedScores
        {
            Id = s.Id,
            FirstName = s.FirstName,
            LastName = s.LastName,
            Languages = Average(s.English, s.French),
            Sciences = Average(s.Biology, s.Chemistry, s.Physics)
        });
}

static void WriteOutput(
    IEnumerable<CombinedScores> combinedScores, string outputPath)
{
    using (var sw = new StreamWriter(outputPath))
    {
        foreach (var scores in combinedScores)
        {
            string outputLine = string.Format(
                "{0:d3} {1} {2} {3:d3} {4:d3} {5:d3}",
                scores.Id, scores.FirstName, scores.LastName,
                scores.Languages, scores.Mathematics, scores.Sciences);

            sw.WriteLine(outputLine);
        }
    }
}

它比您原来的代码更多,但更安全、更清晰、更易于维护。当您完成所有这些操作时,您会意识到只有 ParseScores1CombineScores1 处理输入格式,因此您可以编写 ParseScores2 不需要了解有关输出格式或组合分数的任何内容以及 CombineScores2 .完成此操作后,您的应用程序的主要逻辑可能如下所示:

IEnumerable<CombinedScores> scores;
if (intputFormat1)
    scores = CombineScores1(ParseScores1(inputPath));
else
    scores = CombineScores2(ParseScores2(inputPath));

WriteOutput(scores, outputPath);

【讨论】:

    猜你喜欢
    • 2013-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多