Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

PROGRAMMING CHALLENGE

Name: Anonymous 2010-07-16 18:57


This one should be difficult enough for you guys,

Write a function foo that takes a number n and returns a function that takes a number i, and returns n incremented by i.


My submission, in Scheme


(define (foo num)
  (lambda (x) (+ x num)))

Name: Anonymous 2010-07-16 19:00


(defun foo (n)
  (lambda (i)
    (+ n i)))

Name: Anonymous 2010-07-16 19:02

anyone got a solution for this in C?

Name: 2 2010-07-16 19:10

>>3
Ignoring the "static" solution (not a real one), you could write this in 2 main ways:
1) Unportable: malloc a buffer, and write the function code to it, insert n's value in the function code. Not hard to do, but not portable.
2) Portable: make a FUNCALL-like macro, which calls a function with a context struct or value cell array, and have the function use the values in the struct (in this case, the value of n).

In the case 2, a function is either a piece of code or a structure which contains the address of a real function and an array or structure of values closed over (or pointers to them). A funcall function or macro can call the real function or the encapsulated structure (containing the real function and closed over values). This is how most Lisps do it, but it's done behind the scenes, so you don't really need to know how closures are implemented.

Name: Anonymous 2010-07-16 19:23

Here's my solution in C++0x:


std::function<int(int)> foo(int n) {
   return [](int i) { return i + n; }
}

Name: Anonymous 2010-07-16 19:25

>>3
int (*foo(int n))(int)
{ int f(int i)
  { return n + i; }
  return f; }

Name: Anonymous 2010-07-16 19:28

And here's the old C++ way to do it using templates and compile-time functors, although it's not exactly the same thing as actual lambdas and closures that are in C++0x.


template< int N >
struct foo
{
    int operator()(int i) {
        return i + N;
    }
};

Name: Anonymous 2010-07-16 19:31

>>def foo(n): return (lambda i: i+n)
>>foo(5)(1)
>>6

PyWut?

Name: Anonymous 2010-07-16 19:35

>>6
This only works in C99, not in C89 or C++.

Name: Anonymous 2010-07-16 19:40

>>9
How does the C99 compiler implement it? Or it is undefined, and it's up to the compiler to decide which approach to take?

Name: Anonymous 2010-07-16 19:47

>>10
test.c:

#include <stdio.h>

int (*foo(int n))(int) {
    int f(int i) { return n + i; }
    return f;
}

int main(int argc, char* argv[]) {
    int (*fun)(int) = foo(10);
    printf("%d\n", fun(2));
    return 0;
}


With GCC 4.5 gcc -std=c99 -O2 -S -c test.c, the assembly listing on x86-64 looks like this:


    .file    "test.c"
    .text
    .p2align 4,,15
    .type    f.1848, @function
f.1848:
.LFB4:
    .cfi_startproc
    movl    (%r10), %eax
    addl    %edi, %eax
    ret
    .cfi_endproc
.LFE4:
    .size    f.1848, .-f.1848
    .p2align 4,,15
.globl foo
    .type    foo, @function
foo:
.LFB3:
    .cfi_startproc
    leaq    -36(%rsp), %rax
    ret
    .cfi_endproc
.LFE3:
    .size    foo, .-foo
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string    "%d\n"
    .text
    .p2align 4,,15
.globl main
    .type    main, @function
main:
.LFB5:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $10, %edi
    call    foo
    movl    $2, %edi
    call    *%rax
    movl    $.LC0, %edi
    movl    %eax, %esi
    xorl    %eax, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE5:
    .size    main, .-main
    .ident    "GCC: (GNU) 4.5.0"
    .section    .note.GNU-stack,"x",@progbits

Name: Anonymous 2010-07-16 19:48

EXPERT C SOLUTIONS!
Works for 0 >= n >= 7 . Could be extended for a larger range of n


#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

int (**F)(int);

int F00(int i) { return 0 + i; }
int F01(int i) { return 1 + i; }
int F02(int i) { return 2 + i; }
int F03(int i) { return 3 + i; }
int F04(int i) { return 4 + i; }
int F05(int i) { return 5 + i; }
int F06(int i) { return 6 + i; }
int F07(int i) { return 7 + i; }

int main(int argc, char **argv) {
    unsigned long n,i;
    printf("n:%lu\n",n);
    F = malloc(8*sizeof(void*));
    F[0] = F00;
    F[1] = F01;
    F[2] = F02;
    F[3] = F03;
    F[4] = F04;
    F[5] = F05;
    F[6] = F06;
    F[7] = F07;
    n = argv[1] ? strtoul(argv[1],0,10) : 0;
    i = argv[2] ? strtoul(argv[2],0,10) : 0;
    int (*f)(int) = F[n];
    printf("answer:%d\n",f(i));
    return 0;
}

Name: Anonymous 2010-07-16 19:56

>>12
Wrong. A real EXPERT C89 PROGRAMMER would use the preprocessor.


#define FOO(n) F##n
#define MAKE_FOO(n) int F##n##(int i) { return n + i; }

MAKE_FOO(0)
MAKE_FOO(1)
MAKE_FOO(2)
MAKE_FOO(3)
MAKE_FOO(4)
MAKE_FOO(5)
MAKE_FOO(6)
MAKE_FOO(7)

int main(int argc, char **argv) {
    int (*f)(int) = FOO(2)
    printf("%d\n", f(11));
    return 0;
}

Name: Anonymous 2010-07-16 20:00

>>13
yhbcppt

Name: Anonymous 2010-07-16 20:02

foo = (+)

:/

Name: Anonymous 2010-07-16 20:06

>>11
It looks like it just creates a closure on the stack to capture the value of 'n' that gets passed in when calling the the returned pointer to 'f' (the compiler already knows it's on the stack after the call to foo, so it optimizes it out).

So it looks to be pretty efficient. Not sure yet what would happen across optimization boundaries.

Name: Anonymous 2010-07-16 20:11

DISCLAIMER: THIS IS A TOY! I DO NOT ACTUALLY CODE LIKE THIS

Should run under ikarus with the iksqlite lib


(import (rnrs) (zeptodyne iksqlite))
(define make-counter
  (letrec* ((db (sqlite-open "/tmp/test.db"))
            (n 0)
            (make-name
             (lambda ()
               (set! n (+ n 1))
               (string-append "counter"
                              (number->string n)))))
           (sqlite-exec db "create table retarded_counter (k string, v integer)")
           (lambda (x)
             (let ((name (make-name)))
               (sqlite-exec db (format "insert into retarded_counter values (~s,~s)" name x))
               (lambda ()
                 (let ((ans (string->number
                             (vector-ref
                              (car (sqlite-exec db (format "select v from retarded_counter where k = ~s" name))) 0))))
                   (sqlite-exec db (format "update retarded_counter set v = ~s where k = ~s"
                                           (+ ans 1) name))
                   ans))))))

Name: Anonymous 2010-07-16 20:15

>>17
If this were real code, it would be a candidate submission for thedailywtf.com.

Name: Anonymous 2010-07-16 20:19

>>18
I have similar pieces of code for doing fibs and facts in the toy/ directory of a library I haven't released yet. Vapourware Quality

Name: Anonymous 2010-07-16 20:21

>>19
By toy/, I hope you mean it's just sample code for testing out the underlying library.

Name: Anonymous 2010-07-16 20:23

method(n, block(i, i + n))

Name: Anonymous 2010-07-16 20:28

>>20
Yes and No. I already have real sample code, this is just for fun. I probably won't include it when/if I do release the cod

Name: Anonymous 2010-07-16 20:32

>>20
No, it's because it's a toy language.
nowumad

Name: >>2 2010-07-16 20:37

(defun foo (n) (curry #'+ n))
Should also work, where you should write or import your currying function (a simple 3-4 line version is easy to write, if you don't have it in a library).

Name: Anonymous 2010-07-16 21:26


Func<int, int> foo(int n) {
    return i => n + i
}

Name: Anonymous 2010-07-16 21:39

>>25
What language is that? C#?

Name: Anonymous 2010-07-16 21:45

>>25
This actually looks sexy, minus the confusion due to the return type being grouped with the argument types.

Name: Anonymous 2010-07-16 21:52

>>6
i know this actually works with gcc, but is it supposed to work? does it work with other c compilers?

Name: Anonymous 2010-07-16 21:56

>>28
It works in any C99 compiler, and it only works in gcc if you specifiy -std=c99 or -std=gnu99.

Name: Anonymous 2010-07-16 22:42

>>29
I highly doubt that, considering that nested functions sadly don't feature in any C standard.

Name: Anonymous 2010-07-16 22:58

>>30
Please explain. I can't be trolled(??) properly with insufficient information.

Name: Anonymous 2010-07-16 23:24

>>31
Try comeau online in c99 mode http://www.comeaucomputing.com/tryitout/

I hope you know that if comeau says that something is wrong, then something is wrong?

Name: Anonymous 2010-07-16 23:30


typedef int(*functionPointer)(int);

int n = 20;

int foo2(int i)
{
    return i += n;
}

functionPointer foo(int n)
{
    functionPointer FPtr = foo2;
    FPtr(n);
    return FPtr;
}

int main()
{
    foo(n);
    return 0;
}

Yeah, I cheated with a global variable.
My first post here by the way.

Name: Anonymous 2010-07-16 23:47

>>32
what about real compilers like the intel one or the sun one?

Name: Anonymous 2010-07-16 23:51

>>34
Intel uses the EDG front-end, the same one that Comeau uses, so it's doubtful.

Not sure about Sun.

There's also Clang which is becoming more and more important, not sure about that either.

Name: Anonymous 2010-07-16 23:58

>>35
Clang appears not to support it: http://clang.llvm.org/docs/LanguageExtensions.html

Name: Anonymous 2010-07-17 0:08

>>35
>>36
>clang does not support nested functions; this is a complex feature which is infrequently used, so it is unlikely to be implemented anytime soon.

Name: Anonymous 2010-07-17 0:09

>>37
That's some self-fulfilling prophecy they got going there. Impressive.

Name: Anonymous 2010-07-17 0:14

Nested functions are complex? It should be one addition to the grammar, taking a total of fifteen seconds. What am I missing?

Name: Anonymous 2010-07-17 0:16

>>39
you're forgetting about closures

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List