- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
假设我有两个类:
public class Triangle {
public float Base { get; set; }
public float Height { get; set; }
public float CalcArea() { return Base * Height / 2.0; }
}
public class Cylinder {
public float Radius { get; set; }
public float Height { get; set; }
public float CalcVolume() { return Radius * Radius * Math.PI * Height }
}
我们这里有两个几何形状的描述以及两者的操作。
这是我在 F# 中的尝试:
type Triangle = { Base: float; Height: float }
module TriangleStuff =
let CalcArea t =
t.Base * t.Height / 2.0
type Cylinder = { Radius: float; Height: float }
module CylinderStuff =
let CalcVolume c =
c.Radius * c.Radius * Math.PI * c.Height
假设我对这两个类进行了观察(它们都有 Height
!)并且我想提取一个对任何具有 height 属性的东西都有意义的操作。所以在 C# 中,我可能会提取一个基类并在那里定义操作,如下所示:
public abstract class ShapeWithHeight {
public float Height { get; set; }
public virtual bool CanSuperManJumpOver() {
return Height == TALL; // Superman can *only* jump over tall buildings
}
public const float TALL = float.MaxValue;
}
public class Triangle : ShapeWithHeight {
public float Base { get; set; }
public float CalcArea() { return Base * Height / 2.0; }
public override bool CanSuperManJumpOver() {
throw new InvalidOperationException("Superman can only jump over 3-d objects");
}
}
public class Cylinder : ShapeWithHeight {
public float Radius { get; set; }
public float CalcVolume() { return Radius * Radius * Math.PI * Height }
}
请注意各个子类如何对这个操作的实现有自己的想法。
言归正传,我可能在某个地方有一个函数可以接受 三角形或圆柱体:
public class Superman {
public void JumpOver(ShapeWithHeight shape) {
try {
if (shape.CanSuperManJumpOver()) { Jump (shape); }
} catch {
// ...
}
}
}
.. 这个函数可以接受三角形或圆柱。
我无法将相同的思路应用于 F#。
我一直在阅读一些关于函数式语言的资料。传统的想法是更喜欢表达代数值类型而不是继承类。人们的想法是,最好用较小的构建 block 组合或构建更丰富的类型,而不是从抽象类开始并从那里缩小范围。
在 F# 中,我希望能够定义一个函数,该函数采用已知具有 Height 属性的参数,并以某种方式使用它(即 CanSuperManJumpOver
的基本版本)。我应该如何在功能世界中构建这些类型来实现它?我的问题在功能世界中是否有意义?欢迎对想法发表任何评论。
最佳答案
在我看来,您所描述的 C# 设计从根本上是错误的 - 您使用虚方法 CanSuperManJumpOver
定义了一个类型 ShapeWithHeight
但该方法不能为一个实现的具体实例(2D 三角形),您必须改为抛出异常。
在 F# 中对域建模时的关键原则之一是不应表示无效状态(参见 great article for more)。您的设计打破了这一点 - 因为您可以构造一个三角形并对其调用操作,但该操作无效。
因此,首先要考虑的是,您要建模的实际领域是什么? (这有点难以从你的例子中猜出,但让我试试......)假设你有一些物体,超人可以跳过 3D 形状,但不能跳过 2D 形状。您可以使用可区分联合来区分这两种形状:
type Height = float
type Shape2DInfo =
| Triangle of float * float
type Shape3DInfo =
| Cylinder of float
type Shape =
| Shape2D of Shape2DInfo
| Shape3D of Height * Shape3DInfo
诀窍在于,对于所有 3D 形状,我们现在可以在 Shape3D
情况下直接获得高度 - 因此您始终可以获得 3D 形状的高度(无论它是哪种特定形状 - 这里,只有圆柱体)。对于 2D 形状,我没有包括高度,因为它们可能有也可能没有...
然后您可以编写一个跳跃函数,在形状上进行模式匹配并处理三种不同的情况 - 形状不可跳跃、形状太小或形状足够高:
let jumpOver shape =
match shape with
| Shape2D _ -> printfn "Cannot jump!"
| Shape3D(height, _) ->
if height = Double.MaxValue then printf "Jumped!"
else printfn "Too boring!"
总而言之 - 如果您在 C# 中有一个具有某些属性的抽象类,那么在 F# 中最接近的事情(如果您想使用功能设计,而不是 OO 设计)是使用存储公共(public)属性的类型(Shape
) 并包含另一种类型 (Shape3DInfo
) 的值,该类型指定每个子类的具体细节。
关于C# 到 F# : Functional thinking vs. 多态性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21813250/
我已经编程相当长的时间了,但是整个数据库的事情已经完全被我忽视了。我现在正在尝试了解其中的一些内容,但我正在为如何建立关系而苦苦挣扎。 目的: 我在一家摄像店工作,因此我们有大量需要用元数据标记的文件
exercise 9.3在这本书中,要求读者找出排除this file中最少单词数的5个禁用字母的组合。 . 下面是我对第一部分的解决方案,我觉得对他们来说没有问题 # if the word con
好奇有没有办法让元素认为视口(viewport)是一定宽度的? 这将用于测试响应式 CSS 实用程序。例如这个 CSS component is responsive ,但要查看用户必须缩小浏览器窗口
我一直在磨砺编码刀并重新投入开发。几年前,很多人提到(经典)Mac 上的赛门铁克 Think Pascal 调试器绝对是蜜蜂的膝盖,其他任何地方都没有。我觉得这句话很奇怪,考虑到没有人试图克隆所说的调
Closed. This question needs to be more focused。它当前不接受答案。 5年前关闭。 Locked. This question and its answer
Closed. This question needs to be more focused。它当前不接受答案。 5年前关闭。 Locked. This question and its answer
Closed. This question is opinion-based。它当前不接受答案。 想改善这个问题吗?更新问题,以便editing this post用事实和引用来回答。 5年前关闭。
Closed. This question needs to be more focused。它当前不接受答案。 5年前关闭。 Locked. This question and its answer
我正在尝试找出如何处理更高阶组件的噩梦。我知道它们是如何工作的(我希望如此),但是一旦我开始编写代码,我就会停在这里: const HeadlineHOC = WrappedComponent =>
如何将Mysql的Convert_tz集成到Think sphinx中?我试图寻找答案,但找不到任何答案。我正在使用 ruby - 1.9.3 和 Rails 3.2 思考 sphinx 2.0.
我正在努力理解以下代码片段(位于名为 program.js 的文件中)。我的问题是,我找不到在此文件中声明和/或初始化 CODERBOT_PROG_SAVEONRUN 的位置。似乎没有导入任何外部代码
我有大量的 thinky 模型,我必须在每个文件中为 thinky 创建一个对象,并且它的连接大约 10 次,因为我有那么多模型。 var dbconfig = require('../config/
我有一个模型practice_test_result,我已为其定义了索引,如下所示: ThinkingSphinx::Index.define :practice_test_result, with:
一直在努力解决这个问题...... 表格截图:http://i.stack.imgur.com/PBWMW.jpg 字段名称/ID:姓名 |电邮 |公司编号 |用户类型 Controller : @u
这是我的问题: 我需要在 Scheme 中创建一个程序来玩“我在想什么数字”游戏 参数为下限和上限 每次进行猜测时,都会将其与答案进行比较 如果猜得少,它应该说高一些 如果更多,应该说 -go low
我设置了一个产品模型,我正尝试使用 Thinking Sphinx 进行搜索。该模型有一个名为 status 的属性,在指定日期期间可以是 Active、Not active 或 Active。 我希
我对 Rails ActiveRecord、Doctrine for PHP(以及类似的 ORM)背后的一些设计很感兴趣。 ORM 如何设法实现链式访问器等功能,它们通常需要多深的工作? ORM 如何
我已经编码了几年,但我仍然没有掌握伪编码的窍门,也没有真正用代码思考问题。由于这个问题,我无法弄清楚在创建学习决策树时究竟要做什么。 这是我看过的一些网站相信我还有很多 Decision Tree T
该代码在延迟作业及其在 delay_jobs 表中的制作队列中工作正常,工作人员正在处理它们,但我们仍然在销毁对象时遇到异常 这是痕迹 vendor/bundle/ruby/1.9.1/gems/ri
想知道它们之间有什么区别: indexes shop.created_at, :as =>created_at has shop(:created_at), :as => :created_at ha
我是一名优秀的程序员,十分优秀!