问题描述
在命令式编程中,有简洁的语法糖可以更改对象的一部分,例如分配给字段:
foo.bar = new_value
或者指向数组的元素,或者在某些语言中类似于数组的列表:
a[3] = new_value
在函数式编程中,习惯用法不是更改现有对象的一部分,而是创建一个具有大多数相同值但该字段或元素具有不同值的新对象。
在语义级别上,尽管并非没有取舍,但在简化理解和编写代码方面带来了显着改善。
我在这里询问语法级别的权衡问题。通常,就代码中的外观而言,创建具有大多数相同值但一个字段或元素具有不同值的新对象是一项重量级得多的操作。
是否存在提供语法糖的功能编程语言,以使该操作看起来更简洁?显然,您可以编写一个函数来执行此操作,但是命令式语言提供了语法糖,以使其比调用过程更简洁。是否有任何功能语言都提供语法糖以使其比调用函数更简洁?我可以发誓,至少在某种功能语言中,我至少在object.field实例中看到了语法糖,尽管我忘记了是哪一种。
(此处的性能超出范围。在这种情况下,我仅在谈论代码的外观和功能,而不是代码的执行速度。)
解决方法
Haskell记录具有此功能。您可以将记录定义为:
data Person = Person
{ name :: String,age :: Int
}
和一个实例:
johnSmith :: Person
johnSmith = Person
{ name = "John Smith",age = 24
}
并创建一个交替:
johnDoe :: Person
johnDoe = johnSmith {name = "John Doe"}
-- Result:
-- johnDoe = Person
-- { name = "John Doe"
--,age = 24
-- }
但是,当您必须更新深度嵌套的记录时,此语法很麻烦。我们有一个lens
库,可以很好地解决此问题。
但是,Haskell列表不提供更新语法,因为对列表进行更新将产生O(n)成本-它们是单链接列表。
如果要对类似列表的集合进行有效的更新,可以在数组包中使用Array
,在向量包中使用Vector
。它们都具有用于更新的中缀运算符(//)
:
alteredVector = someVector // [(1,"some value")]
-- similar to `someVector[1] = "some value"`
它不是内置的,但我认为中缀表示法已经足够方便了!
,带有这种糖的一种语言是F#。它可以让你写
let myRecord3 = { myRecord2 with Y = 100; Z = 2 }
Scala也有糖来更新地图:
ms + (k -> v)
ms updated (k,v)
使用Haskell这样的语言,您需要自己编写。如果您可以将更新表示为键值对,则可以定义
let structure' =
update structure key value
或
update structure (key,value)
这将使您可以使用诸如
之类的中缀表示法structure `update` (key,value)
structure // (key,value)
作为概念证明,这是一个可能的(效率低下)实现,如果索引超出范围,则该实现也会失败:
module UpdateList (updateList,(//)) where
import Data.List (splitAt)
updateList :: [a] -> (Int,a) -> [a]
updateList xs (i,y) = let ( initial,(_:final) ) = splitAt i xs
in initial ++ (y:final)
infixl 6 // -- Same precedence as +
(//) :: [a] -> (Int,a) -> [a]
(//) = updateList
使用此定义,["a","b","c","d"] // (2,"C")
返回["a","C","d"]
。并且[1,2] // (2,3)
抛出运行时异常,但我将其留给读者练习。
H。 Rhen举了一个我不知道的Haskell记录语法示例,因此我删除了答案的最后一部分。看看他们的。