【问题标题】:How can one print the name of an identifier or binding in Haskell?如何在 Haskell 中打印标识符或绑定的名称?
【发布时间】:2015-05-11 16:04:34
【问题描述】:

假设我想在 Haskell 中打印一个“变量”的名称和值。这个名字在编译时就知道了!有没有比下面的例子更好的方法?

module V
(e, c, eV, h, hbar, nm, k, viewAllConstants) where
import Text.Printf
c = 2.99792458e8::Double
e = exp(1)
eV = 1.602176565e-19
h = 6.62606957e-34
hbar = h/(2*pi)
nm = 1e-9
k = 1.3806488e-23
viewAllConstants = do
    putStr ((\a b -> (foldl (++) "" ( zipWith (++) a (map (printf " = %.2e\n") b))))
        ["c", "e", "eV", "h", "hbar", "nm", "k"]
        [c, e, eV, h, hbar, nm, k] )

请在您的答案中发布一个工作代码示例 (runhaskell)!

【问题讨论】:

  • 在 Lisp 中,数据和代码之间的区别是模糊的,并且在运行时一切都是可检查的和动态的。在 Haskell 中,所有类型和绑定名称在编译时都会被删除,这允许进行大量优化。
  • 也许你能解释一下你为什么想要这个?根据您所追求的具体目标,Template Haskell 可能会也可能不会提供帮助。
  • @MathematicalOrchid 我认为现在的问题更好地反映了我想要做的事情:打印绑定/标识符的名称而不是值。这个名字在编译时就知道了!
  • @PeterHall:Lisp 函数只接收对其参数值的引用。它不知道 Haskell 函数执行调用者将这些值绑定到哪些标识符(如果有的话!)。

标签: haskell metaprogramming code-inspection


【解决方案1】:

正如@PeterHall 所说:

在 Lisp 中,数据和代码之间的区别是模糊的,并且在运行时一切都是可检查的和动态的。在 Haskell 中,所有类型和绑定名称都会在编译时删除,这允许进行大量优化。

换句话说,名称在运行时是已知的。

此外,这些不是'变量',它们是常量,或者如前所述,绑定。因此,无法访问绑定的名称是没有意义的,因为它永远不会改变。

正如@MathematicalOrchid 所建议的,可能有一个模板 Haskell 解决方案,尽管它可能只是相对有用。

至于打印绑定的更好方法,试试这个:

import Control.Monad (forM_)

viewAllConstants = forM_ (\(a, b) -> putStrLn (a ++ " = " ++ show b)) 
                   $ zip ["c", "e", "eV", "h", "hbar", "nm", "k"] 
                         [ c,   e,   eV,   h,   hbar,   nm,   k ]

【讨论】:

  • 当然这个名字在编译时是已知的......我想你想说点别的,但我不太确定。
  • @ReidBarton 哎呀,该运行了!已编辑。
【解决方案2】:

你可以的!你不能像你想的那样去做。一些语言实现使用字典来表示计算表达式的环境。一些更愚蠢的人将这种机制暴露给程序员。这是愚蠢的,因为它阻止了实现者在未来切换到更快的技术。 Haskell 不会做任何类似的事情,除了可能在类型检查器中。但这并不能阻止您自己构建和使用此类词典!

如果您正在处理一堆相同类型的事情,那么您很幸运——您可以使用 Map 或 trie 将名称与值连接起来。如果您需要各种类型的东西,您的生活就会变得更加复杂——例如,请参阅HList records

说了这么多,我认为你应该仔细考虑一下你到底有多想要这个。这有点不寻常。

【讨论】:

    【解决方案3】:

    我认为您对自己真正想要的东西有些困惑。标识符c的名称就是"c";打印出来没问题。

    我假设您真正想要的是消除模块中["c", "e", "eV", "h", "hbar", "nm", "k"][c,e,eV,h,hbar,nm,k] 之间的重复。具体来说,您打算如何做到这一点?然后我们可以讨论它在Haskell中是否可以工作。

    可能会归结为其中一个或两个

    1. 缺少eval。如果您保留列表["c", "e", "eV", "h", "hbar", "nm", "k"],您可能希望评估这些字符串以找出相应变量的值。虽然您可能会在程序中嵌入 Haskell 解释器,但正如其他人所指出的那样,在运行时不一定有任何名称 "c""e"、...与其值的映射,所以您不能从字符串"c" 到值2.99792458e8

    2. 参考透明度。您可以保留列表[c,e,eV,h,hbar,nm,k],并希望有一些神奇的方法可以从中恢复字符串"c""e"...。但这也是不可能的。 [c,e,eV,h,hbar,nm,k] 只是一个 Doubles 列表,由于 c = 2.99792458e8,我们还有 [c,e,eV,h,hbar,nm,k] = [2.99792458e8,e,eV,h,hbar,nm,k],没有办法从后者中提取 "c"

    【讨论】:

      【解决方案4】:

      假设我想在 Haskell 中打印一个“变量”的名称和值。这个名字在编译时就知道了!

      该名称在某些范围内是已知的,而在其他范围内则不为人所知。这很关键。让我们看看您的示例代码:

      module V (e, c, eV, h, hbar, nm, k, viewAllConstants) where
      
      c = 2.99792458e8
      e = exp(1)
      eV = 1.602176565e-19
      h = 6.62606957e-34
      hbar = h/(2*pi)
      nm = 1e-9
      k = 1.3806488e-23
      
      viewAllConstants = do
          putStr (foldl (++) "" (map ("\n" ++)
                          ( zipWith (++) ["c", "e", "eV", "h", "hbar", "nm", "k"]
                              (map ((" = " ++) . show) [c,e,eV,h,hbar,nm,k])
                          )
      
                      )
              )
      

      putStrfoldlmapzipWithshow 是在 GHC 附带的模块中定义的函数,并且与您的程序分开编译。这些函数无法知道变量的名称——他们得到的只是它们的值。

      请注意,与 Peter Hall 的评论相反,这不是 Haskell 与 Lisp 的区别。在 Lisp 中,过程的参数是通过引用值而不是符号来传递的; Lisp 函数并不比 Haskell 函数更知道其参数的名称。

      此外,您可以将任意表达式作为参数传递给两种语言的函数。这些值未绑定到调用站点中的变量。

      【讨论】:

        猜你喜欢
        • 2011-04-30
        • 1970-01-01
        • 2014-09-29
        • 1970-01-01
        • 2013-11-22
        • 1970-01-01
        • 1970-01-01
        • 2016-03-23
        • 1970-01-01
        相关资源
        最近更新 更多