【问题标题】:Which programming languages implement the concept of a function inverse, such as names() in R哪些编程语言实现了函数逆的概念,比如R中的names()
【发布时间】:2019-03-30 06:33:29
【问题描述】:

在 R 编程语言中,某些函数可以返回一个值,或者如果对它们进行赋值,它们可以设置该值。在下面的示例中,我们创建了一个命名列表,并使用 names() 函数获取这些名称的向量:

> ll <- list(x = 1, y = 2, z = "whatever") # create a list
> names(ll)
[1] "x" "y" "z"

但我可以使用相同的函数来设置这些名称,以一种非常有趣的方式。我分配一个与上面完全相同的形式的新向量:

> names(ll) <- c("a", "b", "c")
> names(ll)
[1] "a" "b" "c"

这里有什么古怪的 R 魔法吗?或者这是计算机科学中的一种技术,可以在其他(深奥的?)语言中看到?我对 DSL 很感兴趣,这个想法似乎很强大,我想进一步研究它。就好像你在说“给我函数的输入,这样输出就是这个”。

这不起作用,但想象一下:

> f <- function(x) x + 1
> f(2)
[1] 3
> z <- 3
> f(z) <- 2
Error in f(z) <- 2 : could not find function "f<-"
> z
[1] 3

我希望 z 等于 1,因为 f(1) 是 2。

这个想法与数学中的反函数概念密切相关。当然,并非所有函数都有逆函数,但由于编程通常有数学基础,我想知道是否在任何其他编程语言中进一步探索了这个概念。

【问题讨论】:

    标签: r functional-programming computer-science


    【解决方案1】:

    通用参考 - Common Lisp

    您的第一个示例更多地是关于 C/C++ 中 lvalues 的概念,对于 Common Lisp,placesGeneralized references是基于宏扩展的,可以由程序员扩展。

    假设您构建了一个 cons 单元 (cons 0 1),假设它绑定到名为 x 的局部变量。一个 cons 单元只是一个带有两个插槽的小结构,具有访问器 carcdr。例如,(car x) 为 0,(cdr x) 为 1。通常,列表是通过链接 cons-cells 构建的,其中 cdr 是子列表。

    改变槽的历史方法是调用RPLACA/RPLACD 函数(replace carreplace cdr)。 SETF 扩展机制是一种讨论地点以及如何影响它们的方式。在 cons-cells 的情况下,您有两个编写器函数,其名称分别为 (setf car)(setf cdr);名称实际上是两个元素的列表(这是函数名称不是符号的唯一情况)。

    然后,您可以编写 (setf (car x) 2) 来改变 x 使其保持值 2。这通过 setf-expansion 被宏扩展为对 RPLACA 的调用。

    其他宏是建立在setf之上的,一般以-f后缀命名,如incf

    (incf (cdr x))
    

    上面增加了 X 的 CDR 的值。 setf 也可用于设置局部变量。

    有趣的是机制可以组合;哈希表的访问器是(gethash &lt;key&gt; &lt;table&gt; &amp;optional &lt;default-value&gt;);数组的访问器是(aref &lt;array&gt; ... &lt;subscripts&gt;)。你可以写:

    (setf (aref (gethash key table) index)
          new-value)
    

    以上内容会改变table中与key关联的数组中位置index的值。

    组合是有效的,因为扩展只遍历嵌套的数据结构,直到结构需要修改的地方;例如,如果您将一棵树 tree 变异为:

    (root-node (node-a 0 1) (node-b 2 3))
    

    那么值2就是根节点的第二个子节点的第一个子节点,就列表位置而言,写成如下:

    (second (third tree)) 
    => 0
    

    如果你想增加那个值,你写:

    (incf (second (third tree)))
    

    INCF 足够聪明,只遍历列表一次;这是宏展开的结果:

    (LET* ((#:LIST (CDR (THIRD TREE))) (#:NEW (+ 1 (CAR #:LIST))))
      (SB-KERNEL:%RPLACA #:LIST #:NEW))
    

    这个机制可以通过调用define-setf-expander来扩展;例如,Cells 库实现了一种约束传播机制,如电子表格公式(数据流、反应式编程),其中对象槽值的更改向下传播到该槽值的用户 (http://stefano.dissegna.me/cells-tutorial.html)。但是对于用户来说,只需调用(setf (slot object) value),它抽象了底层的魔力。

    约束编程 - Prolog

    Prolog 和更普遍的约束编程因允许在多个方向上调用关系而臭名昭著(例如 https://eclipseclp.org/ 解释器):

    lib(fd).
    f(X,R) :- R #= X + 1.
    

    X 为接地且R 左变量的情况:

    [eclipse 3]: f(3,R).
    
    R = 4
    Yes (0.00s cpu)
    

    X 可变且R 接地的情况:

    [eclipse 4]: f(X,4).
    
    X = 3
    Yes (0.00s cpu)
    

    两个都是左变量的情况:

    [eclipse 5]: f(X,Y).
    
    X = X{[-10000000 .. 9999999]}
    Y = Y{[-9999999 .. 10000000]}
    
    Delayed goals:
        -1 - X{[-10000000 .. 9999999]} + Y{[-9999999 .. 10000000]} #= 0
    

    两者都接地的情况:

    [eclipse 6]: f(5,10).
    
    No (0.00s cpu)
    

    【讨论】:

    • 哈!我偷偷怀疑 Prolog 会允许这样做!我将深入解析这个答案,很高兴听到我并没有完全发疯。我对您为什么将 Prolog 的能力描述为“臭名昭著”而不是“令人印象深刻”感兴趣。至于 LISP 方面,我要求提供计算机科学答案,我得到了一个。给我几个月(几年?)来跟上进度。刚从球拍开始。惊人的。非常感谢。
    • @ThomasBrowne 非常感谢您的好话!
    • 这也是Wolfram等计算机代数语言的共同特点。
    【解决方案2】:

    我绝不是 R 语法专家,但来自 Java/OOP 背景,我可以用以下内容解释您的观察:

    > ll <- list(x = 1, y = 2, z = "whatever") # create a list
    > names(ll)                                # call the getter for list names
    > names(ll) <- c("a", "b", "c")            # call the setter for list names
    
    > f <- function(x) x + 1                   # define a function
    > f(2)                                     # call the function
    [1] 3
    > f(3) <- 2                                # makes no sense
    

    也就是说,当names(object) 单独出现或出现在表达式的RHS 上时,R 将调用对象名称的getter。当它出现在分配的 LHS 上时,R 将使用 RHS 上的值调用 setter。

    试图为函数调用的结果赋值是没有意义的。函数通常是一个无状态的东西,所以我们不应该期待你看到的以外的任何东西。

    【讨论】:

    • 也许我的术语不完善。 “倒数”也许可以称为“逆”。数学函数经常(但不总是)有逆。那么一些编程语言应该有吗?这是一个数学上合理的概念。我想知道哪些编程语言支持这个。我怀疑 Prolog 可能会因为它的逻辑编程范式而这样做,但我不够专业,无法知道。是的,当然我可以定义一堆函数,这基本上就是你在 OOP 中所做的,然后很好地隐藏它们。但这并没有真正映射到问题的语义。
    • 问题是,R 中的函数没有任何内存或存储可以说。
    猜你喜欢
    • 2010-09-09
    • 2011-05-04
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 2010-09-17
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    相关资源
    最近更新 更多