函数式编程(柯里)在C /发行与类型 [英] Functional Programming (Currying) in C / Issue with Types

查看:111
本文介绍了函数式编程(柯里)在C /发行与类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为铁杆儿功能程序员,我发现很难不试图鞋拔子我最喜爱的范式到我使用任何语言。在写一些C我发现我想巴结我的功能之一,再通过周围的部分应用功能。阅读之后有没有办法做到用C钻营?和<一听从警告href=\"http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html#Nested-Functions\">http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html#Nested-Functions我想出了:

As a dyed-in-the-wool functional programmer I find it hard not to try to shoehorn my favourite paradigm into whatever language I'm using. While writing some C I found I'd like to curry one of my functions, and then pass around the partially applied function. After reading Is there a way to do currying in C? and heeding the warnings at http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html#Nested-Functions I came up with:

#include <stdio.h>

typedef int (*function) (int);

function g (int a) {
    int f (int b) {
        return a+b;
    }
    return f;
}

int f1(function f){
    return f(1);}

int main () {
    printf ("(g(2))(1)=%d\n",f1(g(2)));
}

这运行正常。但是,我原来的计划与双击和,所以我想我只是改变了相应的类型,我会被罚款:

Which runs as expected. However, my original program works with doubles and so I thought I'd just change the appropriate types and I'd be fine:

#include <stdio.h>

typedef double (*function) (double);

function g (double a) {
    double f (double b) {
        return a+b;
    }
    return f;
}

double f1(function f){
    return f(1);}

int main () {
    printf ("(g(2))(1)=%e\n",f1(g(2)));
}

这会产生这样的话:

bash-3.2$ ./a.out 
Segmentation fault: 11
bash-3.2$ ./a.out 
Illegal instruction: 4

,错误是看似随意的选择。此外,如果两个例子与 -O3 编译编译器本身抛出一个分段故障:11 本身。我没有得到任何来自海湾合作委员会警告,在任何时候,我无法弄清楚发生了什么。有谁知道为什么而第一个没有第二个程序失败?或者更好的是如何解决第二个?

with the choice of error being seemingly random. Furthermore if either example is compiled with -O3 the compiler itself throws a Segmentation fault: 11 itself. I get no warnings from gcc at any point, and I'm not able to figure out what's happening. Does anyone know why the second program fails while the first doesn't? Or better still how to fix the second one?

我的GCC是的i686-苹果darwin11-LLVM-GCC-4.2(GCC)4.2.1(基于苹果公司建立5658)(LLVM建设2336.11.00)我的内核是 Darwin内核版本12.1.0:星期二8月14日十三点29分55秒PDT 2012;根:XNU - 2050年9月2日〜1 / RELEASE_X86_64

My gcc is i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00) and my kernel is Darwin Kernel Version 12.1.0: Tue Aug 14 13:29:55 PDT 2012; root:xnu-2050.9.2~1/RELEASE_X86_64.

编辑:需要明确的是,我明白,我想要做的是愚蠢的。这code是不会在好奇心流动站或纽交所运行。我想了解更多关于如何函数指针在(GNU)C的工作,并解释了一些有趣的事情,我发现。我答应决不做这样的事在现实世界中。

edit: To be clear, I understand that what I'm trying to do is stupid. This code isn't going to run on the Curiosity rover, or the NYSE. I'm trying to understand more about how function pointers in (GNU) C work, and explain something interesting I found. I promise never to do anything like this in the real world.

推荐答案

这是有趣的问题,我把中提到的答案一看纸(的在C的功能可重用性/ C ++ / Objective-C的咖喱功能的)。

An interesting question and I took a look at the paper in the answer cited (More functional reusability in C/C++/Objective-C with Curried functions).

所以,以下为您可能希望去建议的路径。我不认为这是一个真正的咖喱功能,因为我不完全理解什么纸都在谈论,不是功能的程序员。但是,做了一些工作,我发现了一些这方面的概念有趣的应用。我不知道这是你想要在另一方面什么,这样的吹拂我的心灵,你可以在C做到这一点。

So following is a suggested path to where you might want to go. I do not think this is truly a Curried function as I do not fully understand what the paper is talking about, not being a functional programmer. However, doing a bit of work, I find an interesting application of some of this concept. I am not sure this is what you want on the other hand, it kind of blows my mind that you can do this in C.

似乎有两个问题。

首先是要能够处理任意函数任意参数列表调用。我采取的方法是使用标准C库变量参数功能(va_list的用的va_start(),在va_arg()和va_end用来()函数),并在此后函数指针与所提供的参数一起存储到数据区,使他们然后可以在以后的时间被执行。我借用并修改了的printf()函数如何使用格式线知道提供多少个参数和它们的类型。

First of all was to be able to handle arbitrary function calls with arbitrary argument lists. The approach I took was to use standard C Library variable arguments functionality (va_list with the va_start(), va_arg(), and va_end() functions) and to then store the function pointer along with the provided arguments into a data area so that they could then be executed at a later time. I borrowed and modified how the printf() function uses the format line to know how many arguments and their types are provided.

接下来是函数和它的参数列表的存储。我只是用一个结构的一些任意大小只是尝试的概念。这将需要更多的思考。

The next was the storage of the function and its argument list. I just used a struct with some arbitrary size just to try out the concept. This will need a bit more thought.

这个特定版本将使用像堆栈处理数组。有您使用推其自变量的一些任意的功能到堆栈阵列的功能和存在将弹出最上面的功能和它的参数从堆栈阵列和执行它的功能。

This particular version uses an array that is treated like a stack. There is a function that you use to push some arbitrary function with its arguments onto the stack array and there is a function that will pop the top most function and its arguments off of the stack array and execute it.

然而,你可能实际上只是任意结构的物体以某种例如哈希映射的集合,并且可能是非常酷的。

However you could actually just have arbitrary struct objects in some kind of a collection for instance a hash map and that might be very cool.

我只是借的信号处理程序从例如纸张以表明该概念将与该种类的应用程序的工作。

I just borrowed the signal handler example from the paper to show that the concept would work with that kind of an application.

因此​​,这里是源$ C ​​$ c和我希望它可以帮助你拿出一个解决方案。

So here is the source code and I hope it helps you come up with a solution.

您需要其它情况下添加到该交换机以便能够处理其他参数类型。我只是做了一些为概念验证。

You would need to add other cases to the switch so as to be able to process other argument types. I just did a few for proof of concept.

另外,这并不执行函数调用的函数,虽然这似乎在表面上是一个相当简单的扩展。就像我说的,我不完全得到这个咖喱的事情。

Also this does not do the function calling a function though that would seem on the surface to be a fairly straightforward extension. Like I said, I do not totally get this Curried thing.

#include <stdarg.h>
#include <string.h>

// a struct which describes the function and its argument list.
typedef struct {
    void (*f1)(...);
    // we have to have a struct here because when we call the function,
    // we will just pass the struct so that the argument list gets pushed
    // on the stack.
    struct {
        unsigned char myArgListArray[48];   // area for the argument list.  this is just an arbitray size.
    } myArgList;
} AnArgListEntry;

// these are used for simulating a stack.  when functions are processed
// we will just push them onto the stack and later on we will pop them
// off so as to run them.
static unsigned int  myFunctionStackIndex = 0;
static AnArgListEntry myFunctionStack[1000];

// this function pushes a function and its arguments onto the stack.
void pushFunction (void (*f1)(...), char *pcDescrip, ...)
{
    char *pStart = pcDescrip;
    AnArgListEntry MyArgList;
    unsigned char *pmyArgList;
    va_list argp;
    int     i;
    char    c;
    char   *s;
    void   *p;

    va_start(argp, pcDescrip);

    pmyArgList = (unsigned char *)&MyArgList.myArgList;
    MyArgList.f1 = f1;
    for ( ; *pStart; pStart++) {
        switch (*pStart) {
            case 'i':
                // integer argument
                i = va_arg(argp, int);
                memcpy (pmyArgList, &i, sizeof(int));
                pmyArgList += sizeof(int);
                break;
            case 'c':
                // character argument
                c = va_arg(argp, char);
                memcpy (pmyArgList, &c, sizeof(char));
                pmyArgList += sizeof(char);
                break;
            case 's':
                // string argument
                s = va_arg(argp, char *);
                memcpy (pmyArgList, &s, sizeof(char *));
                pmyArgList += sizeof(char *);
                break;
            case 'p':
                // void pointer (any arbitray pointer) argument
                p = va_arg(argp, void *);
                memcpy (pmyArgList, &p, sizeof(void *));
                pmyArgList += sizeof(void *);
                break;
            default:
                break;
        }
    }
    va_end(argp);
    myFunctionStack[myFunctionStackIndex] = MyArgList;
    myFunctionStackIndex++;
}

// this function will pop the function and its argument list off the top
// of the stack and execute it.
void doFuncAndPop () {
    if (myFunctionStackIndex > 0) {
        myFunctionStackIndex--;
        myFunctionStack[myFunctionStackIndex].f1 (myFunctionStack[myFunctionStackIndex].myArgList);
    }
}

// the following are just a couple of arbitray test functions.
// these can be used to test that the functionality works.
void myFunc (int i, char * p)
{
    printf (" i = %d, char = %s\n", i, p);
}

void otherFunc (int i, char * p, char *p2)
{
    printf (" i = %d, char = %s, char =%s\n", i, p, p2);
}

void mySignal (int sig, void (*f)(void))
{
    f();
}

int main(int argc, char * argv[])
{
    int i = 3;
    char *p = "string";
    char *p2 = "string 2";

    // push two different functions on to our stack to save them
    // for execution later.
    pushFunction ((void (*)(...))myFunc, "is", i, p);
    pushFunction ((void (*)(...))otherFunc, "iss", i, p, p2);

    // pop the function that is on the top of the stack and execute it.
    doFuncAndPop();

    // call a function that wants a function so that it will execute
    // the current function with its argument lists that is on top of the stack.
    mySignal (1, doFuncAndPop);

    return 0;
}

您可以有这个一个有趣的附加位是使用由调用的函数doFuncAndPop在 pushFunction()函数() 让你可以把压入堆栈与它的参数另一个函数。

An additional bit of fun you can have with this is to use the pushFunction() function within a function that is invoked by doFuncAndPop() to have another function you can put onto the stack with its arguments.

例如,如果你修改功能 otherFunc()在上面的源如下所示:

For instance if you modify the function otherFunc() in the source above to look like the following:

void otherFunc (int i, char * p, char *p2)
{
    printf (" i = %d, char = %s, char =%s\n", i, p, p2);
    pushFunction ((void (*)(...))myFunc, "is", i+2, p);
}

如果您再添加到 doFuncAndPop另一个呼叫()您将看到第一个 otherFunc()然后执行调用 myFunc的()那是在 pused otherFunc()被执行,然后最后 myFunc的()这是在推通话的main()被调用。

if you then add another call to doFuncAndPop() you will see that first otherFunc() is executed then the call to myFunc() that was pused in otherFunc() is executed and then finally the myFunc() call that was pushed in the main () is called.

编辑2:
如果我们添加的功能,这将执行所有的已经投入到堆栈的功能。这将使我们能够通过推功能和参数上我们的堆栈中,然后执行一系列的函数调用基本上建立了一个小程序。这个功能也可以让我们推一个功能不带任何参数,然后推一些参数。当弹出功能关闭我们的栈,如果参数块没有一个有效的函数指针,那么我们要做的就是把这些参数列表到参数块的堆栈的顶部,并然后执行。类似的改变,可向上面的函数 doFuncAndPop()为好。如果我们使用pushFunction()操作中的执行功能,我们可以做一些有趣的事情。

EDIT 2: If we add the following function this will execute all of the functions that have been put onto the stack. This will allow us to basically create a small program by pushing functions and arguments onto our stack and then executing the series of function calls. This function will also allow us to push a function without any arguments then push some arguments. When popping functions off of our stack, if an argument block does not have a valid function pointer then what we do is to put that argument list onto the argument block on the top of the stack and to then execute that. A similar change can be made to the function doFuncAndPop() above as well. And if we use the pushFunction() operation in an executed function, we can do some interesting things.

实际上,这可能是对一<一个的基础href=\"http://www.scribd.com/doc/19746809/Threaded-Inter$p$ptive-Languages-Their-Design-and-Implementation\"相对=nofollow>螺纹国米preTER 。

Actually this could be the basis for a Threaded Interpreter.

// execute all of the functions that have been pushed onto the stack.
void executeFuncStack () {
    if (myFunctionStackIndex > 0) {
        myFunctionStackIndex--;
        // if this item on the stack has a function pointer then execute it
        if (myFunctionStack[myFunctionStackIndex].f1) {
            myFunctionStack[myFunctionStackIndex].f1 (myFunctionStack[myFunctionStackIndex].myArgList);
        } else if (myFunctionStackIndex > 0) {
            // if there is not a function pointer then assume that this is an argument list
            // for a function that has been pushed on the stack so lets execute the previous
            // pushed function with this argument list.
            int myPrevIndex = myFunctionStackIndex - 1;
            myFunctionStack[myPrevIndex].myArgList = myFunctionStack[myFunctionStackIndex].myArgList;
        }
        executeFuncStack();
    }
}

修改3:
然后,我们进行了更改 pushFunc()来处理双重具有以下附加开关:

EDIT 3: Then we make a change to pushFunc() to handle a double with the following additional switch:

case 'd':
  {
     double d;
     // double argument
     d = va_arg(argp, double);
     memcpy (pmyArgList, &d, sizeof(double));
     pmyArgList += sizeof(double);
   }
break;

所以用这个新功能,我们可以做的东西像下面这样。首先,我们创建类似于原题两种功能。我们将使用pushFunction()一个功能内推论点然后用于由堆栈中的下一功能

So with this new function we can do something like the following. First of all create our two functions similar to the original question. We will use the pushFunction() inside one function to push arguments that are then used by the next function on the stack.

double f1 (double myDouble)
{
    printf ("f1 myDouble = %f\n", myDouble);
    return 0.0;
}

double g2 (double myDouble) {
    printf ("g2 myDouble = %f\n", myDouble);
    myDouble += 10.0;
    pushFunction (0, "d", myDouble);
    return myDouble;
}

新的我们用我们的新功能有以下一系列的语句:

New we use our new functionality with the following series of statements:

double xDouble = 4.5;
pushFunction ((void (*)(...))f1, 0);
pushFunction ((void (*)(...))g2, "d", xDouble);
executeFuncStack();

这些语句会先执行函数 G2()为4.5的值,那么函数 G2()将推动其返回值到我们的堆栈该函数可以使用 F1()这是我们的压入堆栈第一。

These statements will execute first the function g2() with the value of 4.5 and then the function g2() will push its return value onto our stack to be used by the function f1() which was pushed on our stack first.

这篇关于函数式编程(柯里)在C /发行与类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆