- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
考虑两个不可变的类:
public class Student
{
public string Name { get; }
public int Age { get; }
// etc
public IEnumerable<Teacher> Teachers { get; }
// constructor omitted for brevity
// implements structural equality
}
public class Teacher
{
public string Name { get; }
public int Age { get; }
// etc
public IEnumerable<Student> Students { get; }
// constructor omitted for brevity
// implements structural equality
}
public class Student
{
public string Name { get; }
public int Age { get; }
// etc
public IEnumerable<Guid> Teachers { get; }
// constructor omitted for brevity
// implements structural equality
}
public class Teacher
{
public string Name { get; }
public int Age { get; }
// etc
public IEnumerable<Guid> Students { get; }
// constructor omitted for brevity
// implements structural equality
}
// I'm a list of Teachers! You'll always know we all exist.
public IEnumerable<Teacher> Teachers { get; }
// I'm a list of references to Teachers in the state-management layer. Hopefully they exist ¯\_(ツ)_/¯
public IEnumerable<Guid> Teachers { get; }
最佳答案
实现此目的的方法是认识到一个真相要进行一些折衷。您显然希望以一种值得称赞的功能性方式来实现这一目标,但是显然必须发生一些突变才能使单个真实点发生变化以表示新状态。关键问题是如何使它尽可能强大,同时又要使用功能方法。
让我们先从真相开始。
多线程应用程序中的任何单个真点都会出现同步问题。解决该问题的一个好方法是使用锁定,甚至使用STM系统。我将使用language-ext中的STM系统进行此操作(因为它是无锁的,它是一个功能框架,并且还具有您需要的其他许多其他功能:结构记录类型,不可变的集合等)
免责声明:我是language-ext的作者
首先,将学生和教师的收藏品归入不同类型的决定是有问题的。从状态管理的角度来看不是那么多,但是从逻辑的角度来看则更多。最好采用关系数据库方法并将关系移到类型之外:
因此,我们将从创建一个static Database
类开始。 static
表示它是单个事实,但是如果您愿意,可以在实例类中创建:
public static class Database
{
public static readonly Ref<Set<Student>> Students;
public static readonly Ref<Set<Teacher>> Teachers;
public static readonly Ref<Map<Teacher, Set<Student>>> TeacherStudents;
public static readonly Ref<Map<Student, Set<Teacher>>> StudentTeachers;
static Database()
{
TeacherStudents = Ref(Map<Teacher, Set<Student>>());
StudentTeachers = Ref(Map<Student, Set<Teacher>>());
Students = Ref(Set<Student>());
Teachers = Ref(Set<Teacher>());
}
...
Ref
是
the special type for managing the STM system
Map
类似于
Dictionary
但不可变,并具有许多其他有用的功能
Set
类似于
SortedSet
但不可变,并具有许多其他有用的功能
Student
,一组用于
Teacher
。这些是实际记录,然后是
TeacherStudents
和
StudentTeachers
映射到集合。这些就是关系。
Student
和
Teacher
类型现在看起来像这样:
[Record]
public partial class Student
{
public readonly string Name;
public readonly int Age;
}
[Record]
public partial class Teacher
{
public readonly string Name;
public readonly int Age;
}
With
函数(用于不可变转换),默认构造函数等的类型。
public static Unit AddTeacher(Teacher teacher) =>
sync(() =>
{
Teachers.Swap(teachers => teachers.Add(teacher));
TeacherStudents.Swap(teachers => teachers.Add(teacher, Empty));
});
sync
函数在STM系统中启动原子事务。调用
Swap
将管理对值的更改。使用STM系统的好处是,任何同时修改
Database
的并行线程都将检查冲突,并在失败的情况下重新运行事务。这样就可以建立一个更健壮和可靠的更新系统:要么一切正常,要么什么都不做。
Teacher
已添加到
Teachers
,并且
Empty
组的
Student
已添加到
TeacherStudents
关系。
AddStudent
做类似的功能
public static Unit AddStudent(Student student) =>
sync(() =>
{
Students.Swap(students => students.Add(student));
StudentTeachers.Swap(students => students.Add(student, Empty)); // no teachers yet
});
public static Unit AssignStudentToTeacher(Student student, Teacher teacher) =>
sync(() =>
{
// Add the teacher to the student
StudentTeachers.Swap(students => students.SetItem(student, Some: ts => ts.AddOrUpdate(teacher)));
// Add the student to the teacher
TeacherStudents.Swap(teachers => teachers.SetItem(teacher, Some: ss => ss.AddOrUpdate(student)));
});
AddOrUpdate
变为
Remove
:
public static Unit UnAssignStudentFromTeacher(Student student, Teacher teacher) =>
sync(() =>
{
// Add the teacher to the student
StudentTeachers.Swap(students => students.SetItem(student, Some: ts => ts.Remove(teacher)));
// Add the student to the teacher
TeacherStudents.Swap(teachers => teachers.SetItem(teacher, Some: ss => ss.Remove(student)));
});
public static Unit RemoveTeacher(Teacher teacher) =>
sync(() => {
Teachers.Swap(teachers => teachers.Remove(teacher));
TeacherStudents.Swap(teachers => teachers.Remove(teacher));
StudentTeachers.Swap(students => students.Map(ts => ts.Remove(teacher)));
});
public static Unit RemoveStudent(Student student) =>
sync(() => {
Students.Swap(students => students.Remove(student));
StudentTeachers.Swap(students => students.Remove(student));
TeacherStudents.Swap(teachers => teachers.Map(ss => ss.Remove(student)));
});
public static Option<Teacher> FindTeacher(string name, int age) =>
Teachers.Value.Find(new Teacher(name, age));
public static Option<Student> FindStudent(string name, int age) =>
Students.Value.Find(new Student(name, age));
public static Set<Student> FindTeacherStudents(Teacher teacher) =>
TeacherStudents.Value
.Find(teacher)
.IfNone(Empty);
public static Set<Teacher> FindStudentTeachers(Student student) =>
StudentTeachers.Value
.Find(student)
.IfNone(Empty);
public static Set<Student> FindGhostStudents() =>
toSet(StudentTeachers.Value.Filter(teachers => teachers.IsEmpty).Keys);
Database
类型,则可以创建执行写作的子演员和进行阅读的子演员,这实际上取决于该演员的工作量要去做。但是,这带来了额外的复杂性。您可能只有一个write-actor(执行昂贵的工作),然后在更新状态后将其状态发送回父级,以供读者使用。
Database
类型重构为完全不可变的状态值:
[Record]
public partial class Database
{
public static readonly Database Empty = new Database(default, default, default, default);
public readonly Map<Teacher, Set<Student>> TeacherStudents;
public readonly Map<Student, Set<Teacher>> StudentTeachers;
public readonly Set<Student> Students;
public readonly Set<Teacher> Teachers;
public Database AddTeacher(Teacher teacher) =>
With(Teachers: Teachers.Add(teacher),
TeacherStudents: TeacherStudents.Add(teacher, default));
public Database AddStudent(Student student) =>
With(Students: Students.Add(student),
StudentTeachers: StudentTeachers.Add(student, default));
public Database AssignStudentToTeacher(Student student, Teacher teacher) =>
With(StudentTeachers: StudentTeachers.SetItem(student, Some: ts => ts.AddOrUpdate(teacher)),
TeacherStudents: TeacherStudents.SetItem(teacher, Some: ss => ss.AddOrUpdate(student)));
public Database UnAssignStudentFromTeacher(Student student, Teacher teacher) =>
With(StudentTeachers: StudentTeachers.SetItem(student, Some: ts => ts.Remove(teacher)),
TeacherStudents: TeacherStudents.SetItem(teacher, Some: ss => ss.Remove(student)));
public Database RemoveTeacher(Teacher teacher) =>
With(Teachers: Teachers.Remove(teacher),
TeacherStudents: TeacherStudents.Remove(teacher),
StudentTeachers: StudentTeachers.Map(ts => ts.Remove(teacher)));
public Database RemoveStudent(Student student) =>
With(Students: Students.Remove(student),
StudentTeachers: StudentTeachers.Remove(student),
TeacherStudents: TeacherStudents.Map(ss => ss.Remove(student)));
public Option<Teacher> FindTeacher(string name, int age) =>
Teachers.Find(new Teacher(name, age));
public Option<Student> FindStudent(string name, int age) =>
Students.Find(new Student(name, age));
public Set<Student> FindTeacherStudents(Teacher teacher) =>
TeacherStudents
.Find(teacher)
.IfNone(Set<Student>());
public Set<Teacher> FindStudentTeachers(Student student) =>
StudentTeachers
.Find(student)
.IfNone(Set<Teacher>());
public Set<Student> FindGhostStudents() =>
toSet(StudentTeachers.Filter(teachers => teachers.IsEmpty).Keys);
}
Record
代码源来提供
With
函数,以使其更易于转换。
[Union]
discriminated-union code-gen来创建许多消息类型,这些消息类型可以充当actor将执行的操作。这样可以节省很多打字时间!
[Union]
public interface DatabaseMsg
{
DatabaseMsg AddTeacher(Teacher teacher);
DatabaseMsg AddStudent(Student student);
DatabaseMsg AssignStudentToTeacher(Student student, Teacher teacher);
DatabaseMsg UnAssignStudentFromTeacher(Student student, Teacher teacher);
DatabaseMsg RemoveTeacher(Teacher teacher);
DatabaseMsg RemoveStudent(Student student);
DatabaseMsg FindTeacher(string name, int age);
DatabaseMsg FindStudent(string name, int age);
DatabaseMsg FindTeacherStudents(Teacher teacher);
DatabaseMsg FindStudentTeachers(Student student);
DatabaseMsg FindGhostStudents();
}
Setup
和
Inbox
,这应该是不言而喻的:
public static class DatabaseActor
{
public static Database Setup() =>
Database.Empty;
public static Database Inbox(Database state, DatabaseMsg msg) =>
msg switch
{
AddTeacher (var teacher) => state.AddTeacher(teacher),
AddStudent (var student) => state.AddStudent(student),
AssignStudentToTeacher (var student, var teacher) => state.AssignStudentToTeacher(student, teacher),
UnAssignStudentFromTeacher (var student, var teacher) => state.UnAssignStudentFromTeacher(student, teacher),
RemoveTeacher (var teacher) => state.RemoveTeacher(teacher),
RemoveStudent (var student) => state.RemoveStudent(student),
FindTeacher (var name, var age) => constant(state, reply(state.FindTeacher(name, age))),
FindStudent (var name, var age) => constant(state, reply(state.FindStudent(name, age))),
FindTeacherStudents (var teacher) => constant(state, reply(state.FindTeacherStudents(teacher))),
FindStudentTeachers (var student) => constant(state, reply(state.FindStudentTeachers(student))),
FindGhostStudents _ => constant(state, reply(state.FindGhostStudents())),
_ => state
};
}
Inbox
在功能编程中的作用类似于折叠。折叠通常是这样的:
fold :: (S -> A -> S) -> S -> [A] -> S
S
和
A
返回新的
S
(收件箱),
S
初始状态(设置)和要折叠的
[A]
值序列。结果是一个新的
S
状态。
A
值的顺序是消息流。因此,演员可以看作是消息流的折叠。这是一个非常强大的概念。
DatabaseActor
,我们称之为:
ProcessConfig.initialise(); // call once only
var db = spawn<Database, DatabaseMsg>("db", DatabaseActor.Setup, DatabaseActor.Inbox);
tell(db, AddStudent.New(s1));
tell(db, AddStudent.New(s2));
tell(db, AddStudent.New(s3));
tell(db, AddTeacher.New(t1));
tell(db, AddTeacher.New(t2));
tell(db, AssignStudentToTeacher.New(s1, t1));
tell(db, AssignStudentToTeacher.New(s2, t1));
tell(db, AssignStudentToTeacher.New(s3, t2));
ask<Set<Teacher>>(db, FindStudentTeachers.New(s1))
.Iter(Console.WriteLine);
World
的值的流,那么您可以开始看到该可变根的更抽象的视图:您的程序是它执行的操作流的折叠。它的初始状态值是世界的当前状态,并且该操作返回一个新的
World
。这样就不需要突变了。这通常在使用递归的功能应用程序中表示:
public static World RunApplication(World state, Seq<WorldActions> actions) =>
actions.IsEmpty
? state
: RunApplication(RunAction(state, actions.Head), actions.Tail);
World
并返回一个新的
World
,那么您将获得时间的表示形式。
关于c# - 如何为复杂的,不变的模型实现真实的单一来源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60851845/
背景: 我最近一直在使用 JPA,我为相当大的关系数据库项目生成持久层的轻松程度给我留下了深刻的印象。 我们公司使用大量非 SQL 数据库,特别是面向列的数据库。我对可能对这些数据库使用 JPA 有一
我已经在我的 maven pom 中添加了这些构建配置,因为我希望将 Apache Solr 依赖项与 Jar 捆绑在一起。否则我得到了 SolarServerException: ClassNotF
interface ITurtle { void Fight(); void EatPizza(); } interface ILeonardo : ITurtle {
我希望可用于 Java 的对象/关系映射 (ORM) 工具之一能够满足这些要求: 使用 JPA 或 native SQL 查询获取大量行并将其作为实体对象返回。 允许在行(实体)中进行迭代,并在对当前
好像没有,因为我有实现From for 的代码, 我可以转换 A到 B与 .into() , 但同样的事情不适用于 Vec .into()一个Vec . 要么我搞砸了阻止实现派生的事情,要么这不应该发
在 C# 中,如果 A 实现 IX 并且 B 继承自 A ,是否必然遵循 B 实现 IX?如果是,是因为 LSP 吗?之间有什么区别吗: 1. Interface IX; Class A : IX;
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在阅读标准haskell库的(^)的实现代码: (^) :: (Num a, Integral b) => a -> b -> a x0 ^ y0 | y0 a -> b ->a expo x0
我将把国际象棋游戏表示为 C++ 结构。我认为,最好的选择是树结构(因为在每个深度我们都有几个可能的移动)。 这是一个好的方法吗? struct TreeElement{ SomeMoveType
我正在为用户名数据库实现字符串匹配算法。我的方法采用现有的用户名数据库和用户想要的新用户名,然后检查用户名是否已被占用。如果采用该方法,则该方法应该返回带有数据库中未采用的数字的用户名。 例子: “贾
我正在尝试实现 Breadth-first search algorithm , 为了找到两个顶点之间的最短距离。我开发了一个 Queue 对象来保存和检索对象,并且我有一个二维数组来保存两个给定顶点
我目前正在 ika 中开发我的 Python 游戏,它使用 python 2.5 我决定为 AI 使用 A* 寻路。然而,我发现它对我的需要来说太慢了(3-4 个敌人可能会落后于游戏,但我想供应 4-
我正在寻找 Kademlia 的开源实现C/C++ 中的分布式哈希表。它必须是轻量级和跨平台的(win/linux/mac)。 它必须能够将信息发布到 DHT 并检索它。 最佳答案 OpenDHT是
我在一本书中读到这一行:-“当我们要求 C++ 实现运行程序时,它会通过调用此函数来实现。” 而且我想知道“C++ 实现”是什么意思或具体是什么。帮忙!? 最佳答案 “C++ 实现”是指编译器加上链接
我正在尝试使用分支定界的 C++ 实现这个背包问题。此网站上有一个 Java 版本:Implementing branch and bound for knapsack 我试图让我的 C++ 版本打印
在很多情况下,我需要在 C# 中访问合适的哈希算法,从重写 GetHashCode 到对数据执行快速比较/查找。 我发现 FNV 哈希是一种非常简单/好/快速的哈希算法。但是,我从未见过 C# 实现的
目录 LRU缓存替换策略 核心思想 不适用场景 算法基本实现 算法优化
1. 绪论 在前面文章中提到 空间直角坐标系相互转换 ,测绘坐标转换时,一般涉及到的情况是:两个直角坐标系的小角度转换。这个就是我们经常在测绘数据处理中,WGS-84坐标系、54北京坐标系
在软件开发过程中,有时候我们需要定时地检查数据库中的数据,并在发现新增数据时触发一个动作。为了实现这个需求,我们在 .Net 7 下进行一次简单的演示. PeriodicTimer .
二分查找 二分查找算法,说白了就是在有序的数组里面给予一个存在数组里面的值key,然后将其先和数组中间的比较,如果key大于中间值,进行下一次mid后面的比较,直到找到相等的,就可以得到它的位置。
我是一名优秀的程序员,十分优秀!