Jump to page: 1 2
Thread overview
Memoization in compile-time
Mar 13, 2015
Dennis Ritchie
Mar 13, 2015
Rikki Cattermole
Mar 13, 2015
Dennis Ritchie
Mar 13, 2015
Meta
Mar 13, 2015
Dennis Ritchie
Mar 13, 2015
Dennis Ritchie
Mar 13, 2015
Meta
Mar 13, 2015
Ali Çehreli
Mar 13, 2015
Ali Çehreli
Mar 13, 2015
Dennis Ritchie
Mar 13, 2015
Ali Çehreli
Mar 13, 2015
Dennis Ritchie
Mar 13, 2015
weaselcat
Mar 13, 2015
weaselcat
Mar 13, 2015
Ali Çehreli
March 13, 2015
Is it possible to run this code in compile-time?

import std.stdio, std.functional;

ulong fact(ulong n)
{
	alias mfact = memoize!fact;
	return n < 2 ? 1 : n * mfact(n - 1);
}

void main() {

	writeln(fact(10));
}

In CommonLisp variable *factorial-cache* available in CT, and RT. Moreover, in the compiler environment of your *factorial-cache* and RT. Thus we memoires as calculations in CT (constants), and RT.

(eval-always
  (defparameter *factorial-cache* (make-hash-table))
  (defun factorial (n)
    (if (< n 1)
        1
        (setf (gethash n *factorial-cache*) (* n (factorial (1- n)))))))

(define-compiler-macro factorial (&whole whole n)
  (if (constantp n) (factorial n) whole))
March 13, 2015
On 13/03/2015 2:23 p.m., Dennis Ritchie wrote:
> Is it possible to run this code in compile-time?
>
> import std.stdio, std.functional;
>
> ulong fact(ulong n)
> {
>      alias mfact = memoize!fact;
>      return n < 2 ? 1 : n * mfact(n - 1);
> }
>
> void main() {
>
>      writeln(fact(10));
> }
>
> In CommonLisp variable *factorial-cache* available in CT, and RT.
> Moreover, in the compiler environment of your *factorial-cache* and RT.
> Thus we memoires as calculations in CT (constants), and RT.
>
> (eval-always
>    (defparameter *factorial-cache* (make-hash-table))
>    (defun factorial (n)
>      (if (< n 1)
>          1
>          (setf (gethash n *factorial-cache*) (* n (factorial (1- n)))))))
>
> (define-compiler-macro factorial (&whole whole n)
>    (if (constantp n) (factorial n) whole))

Here is an example I have in my Developing with compile time in mind book[0]:

size_t factorial(size_t n) pure {
	assert(n < 11);
	if (n == 0) {
		return 1;
	} else {
		return n * factorial(n - 1);
	}
}

static assert(factorial(5) == 120);

Notice how it is in a static assert? Yeah that is at compile time.
You could assign it to e.g. an enum. Or force it over using meta-programming.

If you haven't already, I would strongly recommend that you read my book on the subject.

[0] https://leanpub.com/ctfe
March 13, 2015
On Friday, 13 March 2015 at 02:38:18 UTC, Rikki Cattermole wrote:
> You could assign it to e.g. an enum. Or force it over using meta-programming.

And this code can be rewritten to D?

template <int n>
struct Factorial
{
    enum { value = n * Factorial<n - 1>::value };
};

template <>
struct Factorial<0>
{
    enum { value = 1 };
};

int main()
{
    constexpr auto x = Factorial<5>::value;
    constexpr auto y = Factorial<7>::value;
}
March 13, 2015
On Friday, 13 March 2015 at 12:49:48 UTC, Dennis Ritchie wrote:
> On Friday, 13 March 2015 at 02:38:18 UTC, Rikki Cattermole wrote:
>> You could assign it to e.g. an enum. Or force it over using meta-programming.
>
> And this code can be rewritten to D?
>
> template <int n>
> struct Factorial
> {
>     enum { value = n * Factorial<n - 1>::value };
> };
>
> template <>
> struct Factorial<0>
> {
>     enum { value = 1 };
> };
>
> int main()
> {
>     constexpr auto x = Factorial<5>::value;
>     constexpr auto y = Factorial<7>::value;
> }

You can translate it directly:

template Factorial(int n)
{
    static if (n == 0)
    {
        enum Factorial = 1;
    }
    else static if (n > 0)
    {
        enum Factorial = n * Factorial!(n - 1);
    }
    else
    {
        static assert(false, "n cannot be negative");
    }
}

int main()
{
    //Calculated at compile time
    auto x = Factorial!5;
    auto y = Factorial!7;
}




However, it's much easier to just write a function and decide whether you want to calculate its value at runtime or compile time.

int factorial(int n)
in
{
    assert(n >= 0, "n cannot be 0");
}
body
{
    return n == 0 ? 1 : n * factorial(n - 1);
}

void main()
{
    //Evaluated at compile time
    enum x = factorial(5);

    //Evaluated at runtime
    auto y = factorial(7);
}
March 13, 2015
On Friday, 13 March 2015 at 12:58:13 UTC, Meta wrote:
> On Friday, 13 March 2015 at 12:49:48 UTC, Dennis Ritchie wrote:
>> On Friday, 13 March 2015 at 02:38:18 UTC, Rikki Cattermole wrote:
>>> You could assign it to e.g. an enum. Or force it over using meta-programming.
>>
>> And this code can be rewritten to D?
>>
>> template <int n>
>> struct Factorial
>> {
>>    enum { value = n * Factorial<n - 1>::value };
>> };
>>
>> template <>
>> struct Factorial<0>
>> {
>>    enum { value = 1 };
>> };
>>
>> int main()
>> {
>>    constexpr auto x = Factorial<5>::value;
>>    constexpr auto y = Factorial<7>::value;
>> }
>
> You can translate it directly:
>
> template Factorial(int n)
> {
>     static if (n == 0)
>     {
>         enum Factorial = 1;
>     }
>     else static if (n > 0)
>     {
>         enum Factorial = n * Factorial!(n - 1);
>     }
>     else
>     {
>         static assert(false, "n cannot be negative");
>     }
> }
>
> int main()
> {
>     //Calculated at compile time
>     auto x = Factorial!5;
>     auto y = Factorial!7;
> }
>
>
>
>
> However, it's much easier to just write a function and decide whether you want to calculate its value at runtime or compile time.
>
> int factorial(int n)
> in
> {
>     assert(n >= 0, "n cannot be 0");
> }
> body
> {
>     return n == 0 ? 1 : n * factorial(n - 1);
> }
>
> void main()
> {
>     //Evaluated at compile time
>     enum x = factorial(5);
>
>     //Evaluated at runtime
>     auto y = factorial(7);
> }

Thanks.
March 13, 2015
On Friday, 13 March 2015 at 12:49:48 UTC, Dennis Ritchie wrote:
> On Friday, 13 March 2015 at 02:38:18 UTC, Rikki Cattermole wrote:
>> You could assign it to e.g. an enum. Or force it over using meta-programming.
>
> And this code can be rewritten to D?
>
> template <int n>
> struct Factorial
> {
>     enum { value = n * Factorial<n - 1>::value };
> };
>
> template <>
> struct Factorial<0>
> {
>     enum { value = 1 };
> };
>
> int main()
> {
>     constexpr auto x = Factorial<5>::value;
>     constexpr auto y = Factorial<7>::value;
> }

confusingly, D uses enum for named compile-time constants.
http://ddili.org/ders/d.en/enum.html

If you take Rikki's example and apply it to an enum(i.e,
enum x = factorial(5);
)

the program will fail to compile if it can't be computed at compile-time.
March 13, 2015
On Friday, 13 March 2015 at 13:16:27 UTC, weaselcat wrote:
> On Friday, 13 March 2015 at 12:49:48 UTC, Dennis Ritchie wrote:
>> On Friday, 13 March 2015 at 02:38:18 UTC, Rikki Cattermole wrote:
>>> You could assign it to e.g. an enum. Or force it over using meta-programming.
>>
>> And this code can be rewritten to D?
>>
>> template <int n>
>> struct Factorial
>> {
>>    enum { value = n * Factorial<n - 1>::value };
>> };
>>
>> template <>
>> struct Factorial<0>
>> {
>>    enum { value = 1 };
>> };
>>
>> int main()
>> {
>>    constexpr auto x = Factorial<5>::value;
>>    constexpr auto y = Factorial<7>::value;
>> }
>
> confusingly, D uses enum for named compile-time constants.
> http://ddili.org/ders/d.en/enum.html
>
> If you take Rikki's example and apply it to an enum(i.e,
> enum x = factorial(5);
> )
>
> the program will fail to compile if it can't be computed at compile-time.

woops, walked away to get coffee before I submitted and got
beaten to the punch by a better answer : )
March 13, 2015
And you can somehow memoization stuff at compile time?
March 13, 2015
On Friday, 13 March 2015 at 13:51:43 UTC, Dennis Ritchie wrote:
> And you can somehow memoization stuff at compile time?

If you want memoization at compile time I would suggest using the template version of Factorial as template instantiations are cached by the compiler. However, std.functional.memoize may work as well at compile time, I don't know.
March 13, 2015
On 03/13/2015 06:16 AM, weaselcat wrote:

> confusingly, D uses enum for named compile-time constants.
> http://ddili.org/ders/d.en/enum.html

Actually, 'static' works as well. I have enumerated the cases here:

    http://ddili.org/ders/d.en/functions_more.html#ix_functions_more.CTFE

<quote>
For a function to be executed at compile time, it must appear in an expression that in fact is needed at compile time:

- Initializing a static variable

- Initializing an enum variable

- Calculating the length of a fixed-length array

- Calculating a template value argument
</quote>

Are there other cases that is worth adding to that list?

Ali

« First   ‹ Prev
1 2