A bit nit picky, but memoization alone won't make the solution optimal, since you are dealing with very large numbers. n! has O(n log n) digits, so in the limit it will take O(n log n) time just to calculate n! from n * (n-1)!.
But I don't know about your big-O estimate. Since the triangle is constructive and you are memoizing calculating n! from n * (n - 1)! will take O(1). It'll be the cached (n - 1)! value times n.
Doing a single multiplication is not O(1) when the numbers have more than a constant number of digits. I'm not precisely sure how long multiplying large numbers takes, but it's at least linear in the number of digits involved. And (n-1)! has O(n log n) digits.
No prob. Sorry if I go on at too much length here ;-)
So a useful formula for counting digits is, a positive integer x has floor(log10(x)) + 1 digits. You can figure this out yourself by thinking, where does the digit-counting function bump up a notch? At 10, 100, 1000, etc. So asymptotically the number of digits in x is O(log x).
So n! has O(log n!) digits. The trickier part is figuring out or knowing that O(n log n) = O(log n!). Using log ab = log a + log b you can expand out the factorial:
In case the last step isn't clear, you can do this splitting-in-half bounding trick. Since each element in the sum is less than log n you can bound from above with
log n + log (n-1) + ... + log 2 + log 1 < n log n
And if you just take the larger half of the list you can bound from below with
Usually, it seems to be either (n log n) or (n log n) - 1 digits.
And usually in this case I would leave of the O, as that usually refers to the performance of an algorithm in time or space. I suppose you could construe the number of digits to be "space" but multiplying O(n) numbers doesn't make that much sense.
Hmm, this solution is not big-O-optimal. The solution you give is O(n^3) if you assume O(1) multiplies and divides, and since you are taking factorials that's probably not that great of an assumption either.
You can improve this by using the recursive formula for Pascal's triangle rather than the closed form. Specifically, n choose k equals (n-1) choose (k-1) + (n-1) choose k. Memoize and you can do (pascal-triangle n) in O(n^2), optimal since that's the amount of prints you have to do.
There is also probably an quicker formula for n choose k mod 2, if you don't have to print the entire triangle. Perhaps try using Legendre's formula for the largest power of a prime that divides a factorial.
Although it is customary to round the number 4.5 up to 5,
in fact 4.5 is no nearer to 5 than it is to 4 (it is 0.5
away from both). When dealing with large sets of
scientific or statistical data, where trends are
important, traditional rounding on average biases the data
upwards slightly. Over a large set of data, or when many
subsequent rounding operations are performed as in digital
signal processing, the round-to-even rule tends to reduce
the total rounding error, with (on average) an equal
portion of numbers rounding up as rounding down. This
generally reduces upwards skewing of the result.
What are these mzscheme functions you are referring to? I was just using a table keyed on thread for threadlocal storage. (Do I need to wrap table access in an atomic-invoke, by the way?)
I wish scheme documentation was more googleable. It's the opposite of javascript, in which you can find 100 ways to do anything, but they are all terribly ugly.
Actually, how did you manage to key on thread? I couldn't figure out how to do this myself, since there isn't a "thread id" or anything that would work as a key...
I would assume (but don't know for sure) that arc tables are thread-safe- I would think you didn't need to worry about atomicity, here...
Thanks for the pointer. Looks like tables are indeed threadsafe. From your link -
"The hash-table-put! procedure is not atomic, but the table is protected by a lock."
As far as threadlocal goes... new-thread returns the thread descriptor and you can use that directly as a hash key.
arc> (= a (table))
#hash()
arc> (= (a (new-thread (fn () (sleep 10)))) 5)
5
arc> a
#hash((#<thread> . 5))
This is inconvenient by itself since in arc you can't access the current thread descriptor without some trickery. It seemed easiest to expose mzscheme's current-thread. I imagine that will have to end up in arc eventually.
As a first cut, I would scan through to find the tempsets, take out the variables, and then expand to a let or with block now that you know their names.
Ah. Right. (And I even worried about similar things when writing make-br-fn...). Well, it would work in simple cases, but let/with are looking like better ideas.
Actually, if we add another primitive (with $ or xdef) which works using mzscheme's namespace-undefine-value!, we could have tempset maintain a global list of tempset variables, and cleartemps go through and undefine them.
Yeah, definitely. But, this specific problem is in the context of writing a restricted eval, like for a browser plugin or something like that. I was thinking of doing a global replace of set with tempset and then cleaning up after one plugin runs. But maybe that is the wrong way to do it.
If you did this for all local variables, and then used namespace-undefine-variable! to delete any defs that had been evaluated, I think you would get a fairly well restricted eval.
I agree with you - I think this is roughly the right idea, I just hadn't found namespace-undefine-variable! when I asked this question.
I think there's still somewhat of a problem with wrapping variables in a "let" with their deep copies - that prevents internal code from wrapping builtins and having other builtins use the wrap, because the let will make the builtin-wrap scope different from the outer scope. But you can do the special-suffix thing in this case.
The difference lies mostly in what the use of the type in 'annotate is. nex-3 views this type as the "real" type, with any "claimed" (user-interface) type ducked by overloading 'isa. I view this type as the "claimed" (user-interface) type, with the "real" type being the underlying implementation (usually a function) for this.
Very minor nitpick: "nex3". The hyphen is only in my URL because nex3.com was taken :-p.
Also, I don't like overriding isa; it's just necessary sometimes because the standard libs aren't really geared towards duck typing. I much prefer overriding basic functions and having more complex functions assume those base functions will work. This is really what duck typing (as I understand it) is all about.
^^; lol. I think I've been rather consistently calling you nex-3 with a hyphen O.O;
The problem, I suppose, is the rather inconsistent formalization/nonformalization of type classes/interfaces/hindley-milner typesystem. In Ruby it's informal - you just say this or that object has this or that function which will just work, in the expected manner. In Haskell and Java it's formal - you say this type provides this type class or presents this interface, and must therefore have this or that function which must work, in the expected manner.
annotated types are kinda like a formal type class, except existing type classes are not trivially extendable except informally (via redef).
Yeah, I think that's right. And I think Arc should go the path of informality, for a couple reasons. First, it's dynamically typed, and duck typing (or informal typing) has historically worked well with dynamically-typed languages (with Ruby, Python, and maybe Smalltalk). CLOS also tends to that side of the spectrum, although I'm not sure how explicitly it embraces duck typing.
Second, and I think more importantly, duck typing gives the programmer more power, in exchange for more opportunity to screw stuff up. This is very much in line with Arc's philosophy.
Using formal interfaces, both the people writing the polymorphic code and the people writing the polymorphic objects have to explicitly code polymorphically. Using informal interfaces, only the people writing polymorphic objects have to be explicit. The other people can* be aware of the polymorphism, which allows powerful stuff like Ruby's Enumerable module, but as long as the objects behave correctly ("quack like a duck"), you can use pass them to code that doesn't expect them at all and they'll still work.
* I know less about Smalltalk than I want to, so I don't know how much it makes use of duck typing.
Here's another interesting bit: car and cdr could be modified into settable-fn's and their defset's could be completely removed from arc.arc ^^. Also, it should be possible to additionally modify compose to work on the '= attachments ^^.
Interesting, I hadn't thought of this. You have to somehow let people still refer to, say, the + function, by its global name. Maybe that could be via some sort of const wrapper thing.
There are still some parts that are stumping me. It seems like I need to either (a) mock annotate to use a different mechanism, or (b) prevent the code from annotating pre-existing variables. The same goes for accessing sig.
Also, I'm not sure how to handle scar/scdr/sref. It don't think places are first-class, so I can't have a table of untouchable places, unless I start working in mzscheme. (Which I would rather avoid.)
It seems like the most plausible way to keep scar/scdr/sref from mutating variables outside the no-side-effects scope is to keep a table of the variables they are allowed to operate on, which is basically anything the user created with cons or table inside the no-side-effects scope. This should invalidate code like
(= a (table))
(no-side-effects (= b a) (= (b "key") "value"))
Is there any more natural way to track this than keeping a table keyed by symbol?
Sorry I was unclear - I was indeed actually trying to ask a hard question.
I don't know a priori what variables this code will try to access. I could possibly figure that out with a macro, but some of the details are still unclear to me. I also don't want side effects if annotate, def, mac, or anything like that was run from the no-side-effect'd code.
At any rate, I think just wrapping in a let is insufficient, even if you do know all the variable names. E.g. you can break out of the sandbox with scdr: