www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Memoization in compile-time

reply "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
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))
Mar 12 2015
parent reply Rikki Cattermole <alphaglosined gmail.com> writes:
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
Mar 12 2015
parent reply "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
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; }
Mar 13 2015
next sibling parent reply "Meta" <jared771 gmail.com> writes:
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); }
Mar 13 2015
next sibling parent "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
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.
Mar 13 2015
prev sibling parent reply "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
And you can somehow memoization stuff at compile time?
Mar 13 2015
next sibling parent "Meta" <jared771 gmail.com> writes:
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.
Mar 13 2015
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/13/2015 06:51 AM, Dennis Ritchie wrote:
 And you can somehow memoization stuff at compile time?
Scarily simple. :D import std.stdio; enum N = 15; enum int[] factorials = memoizeFactorials(N); int[] memoizeFactorials(int n) { if (!__ctfe) { // Make sure that this function is never called at run time assert(false); } int[] result = new int[n]; result[0] = 1; foreach (i; 1 .. n) { result[i] = result[i - 1] * i; } return result; } int fact(int n) { return factorials[n]; } void main() { foreach (i; 0 .. N) { writeln(fact(i)); } } Ali
Mar 13 2015
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/13/2015 11:28 AM, Ali Çehreli wrote:

 enum int[] factorials = memoizeFactorials(N);
Oops! That's generally a trap! The array better be 'static' because a manifest constant like 'enum factorials' would be inserted everywhere it is used. (Similar to a C macro.) I've been scratching my head why the assertion below was failing. It sould be 'static': static int[] factorials = memoizeFactorials(N); If it's an enum, the following assert will fail: foreach (i; 0 .. N) { assert(factorials.ptr + i == &(factorials[i])); } Make it a 'static', it will pass because then there will be just one factorials array. Ali
Mar 13 2015
parent reply "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
On Friday, 13 March 2015 at 18:38:16 UTC, Ali Çehreli wrote:
 On 03/13/2015 11:28 AM, Ali Çehreli wrote:

 enum int[] factorials = memoizeFactorials(N);
Oops! That's generally a trap! The array better be 'static' because a manifest constant like 'enum factorials' would be inserted everywhere it is used. (Similar to a C macro.) I've been scratching my head why the assertion below was failing. It sould be 'static': static int[] factorials = memoizeFactorials(N); If it's an enum, the following assert will fail: foreach (i; 0 .. N) { assert(factorials.ptr + i == &(factorials[i])); } Make it a 'static', it will pass because then there will be just one factorials array. Ali
Thanks. And you can make the same memoized variable was available at compile time and at run time? :)
Mar 13 2015
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 03/13/2015 12:09 PM, Dennis Ritchie wrote:

 And you can make the same memoized variable was available at compile
 time and at run time? :)
Yes, run-time is always possible and if it can be computed at compile time, compile-time is also possible. Ali
Mar 13 2015
parent "Dennis Ritchie" <dennis.ritchie mail.ru> writes:
On Friday, 13 March 2015 at 21:16:24 UTC, Ali Çehreli wrote:
 On 03/13/2015 12:09 PM, Dennis Ritchie wrote:

 And you can make the same memoized variable was available at
compile
 time and at run time? :)
Yes, run-time is always possible and if it can be computed at compile time, compile-time is also possible.
Thanks.
Mar 13 2015
prev sibling parent reply "weaselcat" <weaselcat gmail.com> writes:
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.
Mar 13 2015
next sibling parent "weaselcat" <weaselcat gmail.com> writes:
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 : )
Mar 13 2015
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
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
Mar 13 2015