- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在 Haskell 中编程时,我经常遇到这个问题。在某些时候,我尝试模拟 OOP 方法。在这里,我正在为我发现的一款 Flash 游戏编写某种 AI,我想将各个部分和关卡描述为一个部分列表。
module Main where
type Dimension = (Int, Int)
type Position = (Int, Int)
data Orientation = OrienLeft | OrienRight
data Pipe = Vertical | Horizontal | UpLeft | UpRight | DownLeft | DownRight
data Tank = Tank Dimension Orientation
data Bowl = Bowl Dimension
data Cross = Cross
data Source = Source Dimension
-- desired
-- data Piece = Pipe | Tank | Bowl | Cross | Source
-- So that I can put them in a list, and define
-- data Level = [Piece]
我知道我应该将功能抽象出来并将它们放在一个列表中,但在编写代码的过程中我经常感到受阻。在这些情况下我应该有什么总体心态?
最佳答案
您正在编写一些出色的代码。让我将它再推向类似 Haskell 的解决方案。
您已成功将每个Piece
建模为独立实体。这看起来完全没问题,但您希望能够使用片段集合。最直接的方法是描述一个类型,它可以是任何所需的部分。
data Piece = PipePiece Pipe
| TankPiece Tank
| BowlPiece Bowl
| CrossPiece Cross
| SourcePiece Source
它可以让你写一个像这样的列表
type Kit = [Piece]
但要求在使用 Kit
时,在不同类型的 Piece
上进行模式匹配
instance Show Piece where
show (PipePiece Pipe) = "Pipe"
show (TankPiece Tank) = "Tank"
show (BowlPiece Bowl) = "Bowl"
show (CrossPiece Cross) = "Cross"
show (SourcePiece Source) = "Source"
showKit :: Kit -> String
showKit = concat . map show
还有一个强有力的论据是通过“展平”一些冗余信息来降低 Piece
类型的复杂性
type Dimension = (Int, Int)
type Position = (Int, Int)
data Orientation = OrienLeft | OrienRight
data Direction = Vertical | Horizontal | UpLeft | UpRight | DownLeft | DownRight
data Piece = Pipe Direction
| Tank Dimension Orientation
| Bowl Dimension
| Cross
| Source Dimension
这消除了许多冗余的类型构造函数,但代价是不再能够反射(reflect)函数类型中的片段类型 - 我们不再可以编写
rotateBowl :: Bowl -> Bowl
rotateBowl (Bowl orientation) = Bowl (rotate orientation)
但是相反
rotateBowl :: Piece -> Piece
rotateBowl (Bowl orientation) = Bowl (rotate orientation)
rotateBowl somethingElse = somethingElse
这很烦人。
希望这能突显这两种模型之间的一些权衡。至少有一种“更奇特”的解决方案,它使用类型类和 ExistentialQuantification 来“忘记”除接口(interface)之外的所有内容。这是值得探索的,因为它很诱人,但被认为是 Haskell 反模式。我先描述一下,然后再讨论更好的解决方案。
要使用ExistentialQuantification
,我们删除总和类型Piece
并为片段创建一个类型类。
{-# LANGUAGE ExistentialQuantification #-}
class Piece p where
melt :: p -> ScrapMetal
instance Piece Pipe
instance Piece Bowl
instance ...
data SomePiece = forall p . Piece p => SomePiece p
instance Piece SomePiece where
melt (SomePiece p) = melt p
forgetPiece :: Piece p => p -> SomePiece
forgetPiece = SomePiece
type Kit = [SomePiece]
meltKit :: Kit -> SomePiece
meltKit = combineScraps . map melt
这是一种反模式,因为 ExistentialQuantification
会导致更复杂的类型错误并删除大量有趣的信息。通常的论点是,如果你要删除除了熔化
碎片
之外的所有信息,你应该一开始就熔化它。
myScrapMetal :: [ScrapMetal]
myScrapMetal = [melt Cross, melt Source Vertical]
如果您的类型类具有多个函数,那么您真正的功能可能存储在该类中。例如,假设我们可以熔化
一 block 碎片
并出售
它,也许更好的抽象如下
data Piece = { melt :: ScrapMetal
, sell :: Int
}
pipe :: Direction -> Piece
pipe _ = Piece someScrap 2.50
myKit :: [Piece]
myKit = [pipe UpLeft, pipe UpRight]
老实说,这几乎正是您通过 ExistentialQuantification
方法得到的结果,但更直接。当您通过forgetPiece
删除类型信息时,您只留下class Piece
的类型类字典——这正是类型类中函数的产物,这就是我们的使用刚刚描述的数据 block
类型重新显式建模。
我能想到使用 ExistentialQuantification
的一个原因是 Haskell 的 Exception
系统最好的例证 - 如果您有兴趣,请看一下它是如何实现的。缺点是必须设计Exception
,以便任何人都可以在任何代码中添加新的Exception
,并使其可通过共享 Control.Exception
机制,同时保持足够的身份以便用户捕获它。这还需要 Typeable
机制......但这几乎肯定是矫枉过正。
要点应该是,您使用的模型在很大程度上取决于您最终如何使用数据类型。将所有内容表示为抽象 ADT(例如数据片解决方案)的初始编码很好,因为它们会丢弃很少的信息……但也可能既笨重又缓慢。像 melt
/sell
字典这样的最终编码通常更高效,但需要更深入地了解 Piece
的“含义”以及它的含义使用过。
关于oop - 在 Haskell 中编程时如何纠正我的 OOP 倾向,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18867620/
我正在尝试打印 timeval 类型的值。实际上我可以打印它,但我收到以下警告: 该行有多个标记 格式“%ld”需要“long int”类型,但参数 2 的类型为“struct timeval” 程序
我正在编写自己的 unix 终端,但在执行命令时遇到问题: 首先,我获取用户输入并将其存储到缓冲区中,然后我将单词分开并将它们存储到我的 argv[] 数组中。IE命令是“firefox”以启动存储在
我是 CUDA 的新手。我有一个关于一个简单程序的问题,希望有人能注意到我的错误。 __global__ void ADD(float* A, float* B, float* C) { con
我有一个关于 C 语言 CGI 编程的一般性问题。 我使用嵌入式 Web 服务器来处理 Web 界面。为此,我在服务器中存储了一个 HTML 文件。在此 HTML 文件中包含 JavaScript 和
**摘要:**在代码的世界中,是存在很多艺术般的写法,这可能也是部分程序员追求编程这项事业的内在动力。 本文分享自华为云社区《【云驻共创】用4种代码中的艺术试图唤回你对编程的兴趣》,作者: break
我有一个函数,它的任务是在父对象中创建一个变量。我想要的是让函数在调用它的级别创建变量。 createVariable testFunc() [1] "test" > testFunc2() [1]
以下代码用于将多个连续的空格替换为1个空格。虽然我设法做到了,但我对花括号的使用感到困惑。 这个实际上运行良好: #include #include int main() { int ch, la
我正在尝试将文件写入磁盘,然后自动重新编译。不幸的是,某事似乎不起作用,我收到一条我还不明白的错误消息(我是 C 初学者 :-)。如果我手动编译生成的 hello.c,一切正常吗?! #include
如何将指针值传递给结构数组; 例如,在 txt 上我有这个: John Doe;xxxx@hotmail.com;214425532; 我的代码: typedef struct Person{
我尝试编写一些代码来检索 objectID,结果是 2B-06-01-04-01-82-31-01-03-01-01 . 这个值不正确吗? // Send a SysObjectId SNMP req
您好,提前感谢您的帮助, (请注意评论部分以获得更多见解:即,以下示例中的成本列已添加到此问题中;西蒙提供了一个很好的答案,但成本列本身并未出现在他的数据响应中,尽管他提供的功能与成本列一起使用) 我
我想知道是否有人能够提出一些解决非线性优化问题的软件包的方法,而非线性优化问题可以为优化解决方案提供整数变量?问题是使具有相等约束的函数最小化,该函数受某些上下边界约束的约束。 我已经在R中使用了'n
我是 R 编程的初学者,正在尝试向具有 50 列的矩阵添加一个额外的列。这个新列将是该行中前 10 个值的平均值。 randomMatrix <- generateMatrix(1,5000,100,
我在《K&R II C 编程 ANSI C》一书中读到,“>>”和“0; nwords--) sum += *buf++; sum = (sum >>
当下拉列表的选择发生变化时,我想: 1) 通过 div 在整个网站上显示一些 GUI 阻止覆盖 2)然后处理一些代码 3) 然后隐藏叠加层。 问题是,当我在事件监听器函数中编写此逻辑时,将执行 onC
我正在使用 Clojure 和 RESTEasy 设计 JAX-RS REST 服务器. 据我了解,用 Lisp 系列语言编写的应用程序比用“传统”命令式语言编写的应用程序更多地构建为“特定于领域的语
我目前正在研究一种替代出勤监控系统作为一项举措。目前,我设计的用户表单如下所示: Time Stamp Userform 它的工作原理如下: 员工将选择他/她将使用的时间戳类型:开始时间、超时、第一次
我是一名学生,试图自学编程,从在线资源和像您这样的人那里获得帮助。我在网上找到了一个练习来创建一个小程序来执行此操作: 编写一个程序,读取数字 a 和 b(长整型)并列出 a 和 b 之间有多少个数字
我正在尝试编写一个 shell 程序,给定一个参数,打印程序的名称和参数中的每个奇数词(即,不是偶数词)。但是,我没有得到预期的结果。在跟踪我的程序时,我注意到,尽管奇数词(例如,第 5 个词,5 %
只是想知道是否有任何 Java API 可以让您控制台式机/笔记本电脑外壳上的 LED? 或者,如果不可能,是否有可能? 最佳答案 如果你说的是前面的 LED 指示电源状态和 HDD 繁忙状态,恐怕没
我是一名优秀的程序员,十分优秀!