首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >函数式编程库

函数式编程库
EN

Code Review用户
提问于 2015-06-22 13:26:52
回答 1查看 369关注 0票数 8

我已经开始在C#中建立一个函数式编程库,这在某种程度上受到了scalaz和Scala中的函数式编程的启发。

我希望得到一些关于Monad实现和评论的反馈。

请理解这是在C#和.NET中实现的,它们不支持类型分类和更高的索引。

这里是我到目前为止能够用开发出来的东西创建的一个示例演示。

代码语言:javascript
复制
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;

namespace Sharper.Tests
{
    internal class Student
    {
        public int Id{ get; set; }

        public string Name{ get; set; }
    }

    internal class Score
    {
        public int StudentId{ get; set; }

        public string Name{ get; set; }

        public Decimal Result{ get; set; }
    }

    internal class ScoreServer
    {

        public ScoreServer()
        {
            students = new Dictionary<int, Student>();
            scores = new Dictionary<int, List<Score>>();

            students.Add(1, new Student{ Id = 1, Name = "Blair" });
            students.Add(2, new Student{ Id = 2, Name = "Esther" });

            scores.Add(1, new List<Score> { 
                new Score{ StudentId = 1, Name = "Maths", Result = 76 },
                new Score{ StudentId = 1, Name = "English", Result = 56 },
                new Score{ StudentId = 1, Name = "Science", Result = 67 }
            });

            scores.Add(2, new List<Score> { 
                new Score{ StudentId = 2, Name = "Maths", Result = 87 },
                new Score{ StudentId = 2, Name = "English", Result = 72 },
                new Score{ StudentId = 2, Name = "Science", Result = 59 }
            });
        }

        public IO<String> GetStudentScoreFor(int studentId, string classname)
        {
            var studentT = GetStudentById(studentId).OptionT();
            var studentNameT = studentT.Map(x => x.Name)
                                       .OrElse(new Some<string>(studentId.ToString()));

            var matchedScoreT = from student in studentT
                                         from score in GetClassScoreByStudent(student.ToOption(), classname).OptionT()
                                         select String.Format("Student: {0} Recieved {1} for {2}", student.Name, score.Name, score.Result);

            var result = from name in studentNameT
                                  from text in matchedScoreT.Map(score => score)
                                                   .OrElse(String.Format("Student {0} has not record for {1}", name, classname)
                                                   .ToOption())
                                  select text;

            return result.GetValueOrDefault(String.Empty);
        }

        public IO<Option<Student>> GetStudentById(int studentId)
        {
            return new IO<Option<Student>>(
                () => {
                    Thread.Sleep(TimeSpan.FromSeconds(1.5)); // Simulate work
                    return students.SafeGet(studentId);
                }
            );
        }

        public IO<Option<Score>> GetClassScoreByStudent(Option<Student> student, string @class)
        {
            return new IO<Option<Score>>(
                () => {
                    Thread.Sleep(TimeSpan.FromSeconds(1)); // Simulate work
                    return student.FlatMap(x => scores.SafeGet(x.Id)
                                  .FlatMap(y => y.FirstOrDefault(z => z.Name == @class)
                                  .ToOption()));
                }
            );
        }

        private Dictionary<int,Student> students{ get; set; }

        private Dictionary<int, List<Score>> scores { get; set; }
    }

    [TestFixture]
    public class OptionIODemoTests
    {

        [Test]
        public void Testing_ScoreServer1()
        {
            var server = new ScoreServer();
            var result = server.GetStudentScoreFor(1, "Maths").PerformUnsafeIO();

            Assert.AreEqual(String.Format("Student: {0} Recieved {1} for {2}", "Blair", "Maths", 76), result);
        }

        [Test]
        public void Testing_ScoreServer2()
        {
            var server = new ScoreServer();
            var result = server.GetStudentScoreFor(1, "Mathematics").PerformUnsafeIO();

            Assert.AreEqual(String.Format("Student {0} has not record for {1}", "Blair", "Mathematics"), result);
        }

        [Test]
        public void Testing_ScoreServer3()
        {
            var server = new ScoreServer();
            var result = server.GetStudentScoreFor(3, "Mathematics").PerformUnsafeIO();

            Assert.AreEqual(String.Format("Student {0} has not record for {1}", "3", "Mathematics"), result);
        }
    }

}

主要项目站点是这里

EN

回答 1

Code Review用户

发布于 2015-06-29 21:38:45

首先,这个Dictionary吸引了我的眼球:

代码语言:javascript
复制
students = new Dictionary<int, Student>();
students.Add(1, new Student{ Id = 1, Name = "Blair" });
students.Add(2, new Student{ Id = 2, Name = "Esther" });

那么,您有一个字典,其中键已经包含在ID中,如果值是?为什么不把它变成一个普通的Dictionary<int, string>呢?如果保证键无论如何不会重复,您可以为简单起见设置一个List<Student>

然而,从这个角度来看,似乎我们必须为每个学生保留一组Scores,而您通过保留两个列表来做到这一点。我会把这些组合成一个单子,一个Student,一个IdName,和一个List of Scores,然后,你可以把学生和他们的成绩放在一起。然而,这在允许复制Ids方面存在困难,因此也许Dictionary是这里最好的选择,即使Id在关键和价值上都是如此。

Testing_ScoreServer1()的描述性不是很强。我必须阅读测试才能弄清楚它到底是什么测试。稍后,当您进行使测试失败的更改时,您如何在不阅读代码的情况下知道什么是实际失败的?我会给它起一个描述性很强的名字,我可以从失败的测试列表中分辨出失败的原因。

新分数{ StudentId = 1,名称=“数学”,结果= 76 },新分数{ StudentId = 1,Name = "English",结果= 56 },新分数{ StudentId = 1,Name = "Science",结果= 67 }

在测试中使用值76,以及使用值"Blair"。如果你决定不喜欢76这个数字,然后把它改成75呢?那会打破你的测试。您应该考虑将这个值设置在一个地方作为一个变量,因此您只需要在一个地方更改它的值。也许你可以有一个List的学生,你从数据和使用您的所有考试?

票数 7
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/94330

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档