【问题标题】:update list enclosed in tuple更新包含在元组中的列表
【发布时间】:2011-12-29 12:11:03
【问题描述】:

基本上我想实现一个包含可以更新的列表的元组(或任何适合我的问题的数据类型)。由于不可变性等原因,下面的演示代码当然不起作用。这只是为了更好地说明我想要实现的目标。

我想知道有什么优雅的方法、数据类型(IOREfs?)或模块可以为我做到这一点,或者在这种情况下,我真的需要拆开整个元组,并用更新的元素重建它。

顺便说一句:直觉上,我预计至少只要不打印测试,就不会评估列表消息。

test = ("192.168.1.1", 3455, (1234566, msgs))
msgs = ["aaa", "bbbb", "ccccc"]

main = do
     --print test
     let msg_tmp = "first" : msgs
     let msgs = msg_tmp
     print msgs
     print test 

【问题讨论】:

  • 您真的要求可变性,还是只是想要更简单的方法来创建一些不可变数据的新版本?在前一种情况下,请重新考虑。特别是如果您来自一种允许可变性的语言。
  • 定义“成本”。你不担心性能,是吗?如果是这样,你会感到羞耻。回去学习;)
  • 您可能走错了路,在 io 和 st monad 中有可变数据结构,但除非您需要,否则让 ghc 为您整理并使用纯功能数据结构。跨度>
  • @delnan 我不认为像“为你感到羞耻”这样的 cmets 支持任何事情或任何人。
  • 正如笑脸所表明的那样(或者我希望如此),我是在开玩笑。但说真的,你似乎在担心非问题。除了通常的“微优化是万恶之源”之外,GHC 编译的 Haskell 通常非常快,即使是所有不可变的数据也是如此。只需编写惯用的 Haskell 代码 - 这涉及大量的不变性,可变性的选项在以后引入并非常谨慎地使用。

标签: haskell


【解决方案1】:

正如其他人所说,不以实用的方式做事应该有非常具体和重要的理由。您的元组似乎不必要地令人费解,但您可以使用以下额外消息创建一个新元组:

addMsg (a,b,(c,msgs)) msg = (a,b,(c,msg:msgs))

【讨论】:

    【解决方案2】:

    正如您所说,Haskell 使用不可变数据类型。有 STRefs 和 IORefs 可以做类似可变的事情,但如果你正在学习 Haskell,那么你想先掌握做不可变事情的窍门。

    Haskell 确实有记录访问器,但没有用于元组。如果你要写这样的东西:

    data MyRecord {
       ipAddress :: String,
       magicNumber :: Integer,
       messages :: [String]
    } deriving Show
    

    然后您可以使用字段名称作为字段上的获取函数。给定

    test = MyRecord "192.168.1.1" 12345 ["aaaa", "bbbb", "cccc"]
    

    然后您可以使用表达式“messges test”或在 IO monad 中获取消息:

    print $ messages test
    

    您可以使用一些特殊的语法糖创建一个新版本的“测试”,如下所示:

    test2 = test {magicNumber = 654321}
    

    这会将 test2 设置为 test 的副本,但具有不同的 magicNumber 字段。

    所以你可以说:

    test3 = test {messages = "first" : messages test}
    

    我同意这有点笨拙,但实际上你并不经常需要它。

    有一些关于改进 Haskell 记录系统的建议,其中上述语法糖实际上变成了一个函数,但其​​中任何一个都没有达成共识。您可能应该在必要时编写自己的修饰符,例如

    modifyMessages :: ([String] -> [String]) -> MyRecord -> MyRecord
    modifyMessages =  -- Left as an exercise for the student
    

    然后你可以实现你的例子

    test4 = modifyMessges ("first" :) test
    

    【讨论】:

    • 谢谢。事实上,Haskell 代码通常看起来非常漂亮,以至于我无法相信像 test1、test2、test3 等这样“笨拙”的东西真的是真的。我只是希望能找到一些我还没有遇到过的非常优雅的东西。
    【解决方案3】:

    您是否考虑过使用lenses?如果您的目的是让程序员更新元组变得容易和透明,那么它们非常适合。下面使用优秀的fclabels包:

    import Data.Label
    import Control.Category
    import Prelude hiding ((.))
    
    
    test = ("192.168.1.1", 3455, (1234566, msgs))
    msgs = ["aaa", "bbbb", "ccccc"]
    
    append x = modify messages (x :)
    
    main = do
         let test' = append "first" test
         print test'
         print (get messages test')
    
    second2 = lens (\(a,b) -> b)   (\b (a,_) -> (a,b))
    third3  = lens (\(a,b,c) -> c) (\c (a,b,_) -> (a,b,c))
    messages = second2 . third3
    

    second2third3 可以是通用函数 为您需要的所有元组定义一次,或者它们可以是某物 使用有意义的名称,例如 messagestimestamp。总之这个 是写一次,然后隐藏在库中的东西。

    使用 GHCi 进行测试得到了预期的结果:

    ghci> :main
    
    ("192.168.1.1",3455,(1234566,["first","aaa","bbbb","ccccc"])) 
    ["first","aaa","bbbb","ccccc"]
    

    【讨论】:

      猜你喜欢
      • 2012-10-28
      • 2016-06-27
      • 1970-01-01
      • 2016-09-25
      • 2019-12-01
      • 2016-04-17
      • 2020-12-25
      • 2020-01-07
      • 1970-01-01
      相关资源
      最近更新 更多