Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using callCC with higher-order functions in R

I'm trying to figure out how to get R's callCC function for short-circuiting evalutation of a function to work with functions like lapply and Reduce.

Motivation

This would make Reduce and and lapply have asymptotic efficiency > O(n), by allowing you to exit a computation early.

For example, if I'm searching for a value in a list I could map a 'finder' function across the list, and the second it is found lapply stops running and that value is returned (much like breaking a loop, or using a return statement to break out early).

The problem is I am having trouble writing the functions that lapply and Reduce should take using a style that callCC requires.

Example

Say I'm trying to write a function to find the value '100' in a list: something equivalent to

imperativeVersion <- function (xs) {
    for (val in xs) if (val == 100) return (val)
}

The function to pass to lapply would look like:

find100 <- function (val) { if (val == 100) SHORT_CIRCUIT(val)  }
functionalVersion <- function (xs) lapply(xs, find100)

This (obviously) crashes, since the short circuiting function hasn't been defined yet.

callCC( function (SHORT_CIRCUIT) lapply(1:1000, find100) )

The problem is that this also crashes, because the short circuiting function wasn't around when find100 was defined. I would like for something similar to this to work.

the following works because SHORT_CIRCUIT IS defined at the time that the function passed to lapply is created.

callCC(
    function (SHORT_CIRCUIT) {
        lapply(1:1000, function (val) {
             if (val == 100) SHORT_CIRCUIT(val)
        })
)

How can I make SHORT_CIRCUIT be defined in the function passed to lapply without defining it inline like above?

I'm aware this example can be achieved using loops, reduce or any other number of ways. I am looking for a solution to the problem of using callCC with lapply and Reduce in specific.

If I was vague or any clarification is needed please leave a comment below. I hope someone can help with this :)

Edit One: The approach should be 'production-quality'; no deparsing functions or similar black magic.

like image 773
Róisín Grannell Avatar asked Oct 02 '22 16:10

Róisín Grannell


1 Answers

I found a soluton to this problem:

find100 <- function (val) {
    if (val == 100) SHORT_CIRCUIT(val)
}

short_map <- function (fn, coll) {


    callCC(function (SHORT_CIRCUIT) {

        clone_env <- new.env(parent = environment(fn))
        clone_env$SHORT_CIRCUIT <- SHORT_CIRCUIT

        environment(fn) <- clone_env
        lapply(coll, fn)

    })
}

short_map(find100, c(1,2,100,3))

The trick to making higher-order functions work with callCC is to assign the short-circuiting function into the input functions environment before carrying on with the rest of the program. I made a clone of the environment to avoid unintended side-effects.

like image 168
Róisín Grannell Avatar answered Oct 18 '22 04:10

Róisín Grannell