2

Below is a simplified scenario of a problem that I'm running into. There are 3 functions each wrapped with BaseForm[] and a default base of 10 is passed to each through the optional f parameter. Each function depends on the function before it.

Add[a_, b_, f_:10] := BaseForm[a + b, f]
MultSum[a_, b_, c_, f_:10] := BaseForm[Add[a, b]*c, f]
ExpMultSum[a_, b_, c_, d_, f_:10] := BaseForm[MultSum[a, b, c]^d, f]

Please be aware the functions above have nothing to do with what I'm trying to accomplish. They only exist to highlight the issue that I am encountering.

I intentionally avoid passing in the f to the earlier functions because the output format isn't parseable by the caller. Add[] works fine, as does MultSum[], but once we get to ExpMultSum[] the Mathematica 9 parser appears to hit some internal limitation and stops evaluating the expression:

Add[2, 2]
4

MultSum[2,2,2]
8

ExpMultSum[2, 2, 2, 2]
8²

My best guess is that there is a nesting limitation? The only solution I can come up with is to move the BaseForm outside the function and wrap each call (ex. BaseForm[Add[2, 2],10]). Is there a known workaround so I can keep the BaseForm internal to the function?

Dustin Darcy
  • 107
  • 6
  • 1
    BaseForm[] is only intended for output formatting. Why not do your arithmetic conventionally, and reserve BaseForm[] when you're ready to display output? – J. M.'s missing motivation Nov 26 '15 at 05:55
  • That's how I was originally doing it, but to save myself from having to constantly type BaseForm[expression,n] it was convenient to add it to the function itself. – Dustin Darcy Nov 26 '15 at 05:58
  • 2
    And that worked out well for you, eh? You can set up $PrePrint to have your output be always printed in base $k$ form, for some globally set value of k. – J. M.'s missing motivation Nov 26 '15 at 06:17
  • It works for two levels of depth. =P The base changes quite a bit. So sadly $PrePrint wouldn't do the trick, but it's good to know the option exists. – Dustin Darcy Nov 26 '15 at 06:20
  • You change the globally set value of the base as appropriate. Unless there's something you're not telling, you shouldn't need to perform any conversions if you're just doing arithmetic. – J. M.'s missing motivation Nov 26 '15 at 06:52
  • Wouldn't that create a scoping problem? Either we leave $PrePrint always set or it has to be unset before the return value. Add[a_, b_, f_: 10] := Module[{}, $PrePrint = BaseForm[#1, f] &; Return[a + b]; $PrePrint =.] But that isn't possible before the return unless there is a mechanism to defer execution or to always execute cleanup code following a module call. – Dustin Darcy Nov 26 '15 at 08:44
  • Somehow I feel there's something you're missing. Are you aware that all arithmetic is done internally in binary anyway, and you thus don't need to set the base up until you need to display your result in the front end? – J. M.'s missing motivation Nov 26 '15 at 09:11
  • Imagine we want to run Add[] and sometimes view it in Base-2. Then in other scenarios we want to use it normally in Base-10. Add[] is a primitive for more complicated operations where presentation isn't necessary. So there is no need to pass in a BaseForm value. Add[] gets used in MultSum[], which we occasionally want to view in a different base, and MultSum[] gets used in ExpMultSum[] which we also would like to sometimes view in something other than base-10. Putting the BaseForm[] in the function is purely a macro to save keystrokes and for readability of long expressions. – Dustin Darcy Nov 26 '15 at 09:25
  • Why should your two more complicated functions have to take input in a different base? A number's a number, regardless of representation. – J. M.'s missing motivation Nov 26 '15 at 10:06
  • I think I see the confusion. They don't. MultSum[a_, b_, c_, f_:10] := BaseForm[Add[a, b]*c, f]. Notice the "base" of MultSum's f isn't passed in to Add[]. Add[] uses the default value of f:10 as defined in the function declaration Add[a_, b_, f_:10]. – Dustin Darcy Nov 26 '15 at 10:10
  • 2
    (1) This is not the result of any internal limitation. It is a consequence of applying operations intended for arithmetic expressions instead to expressions of the form BaseForm[...]. If you do FullForm[...] of the outputs this will become more clear. – Daniel Lichtblau Nov 26 '15 at 15:25
  • 2
    (2) In essence this is an oft-duplicated question, though usually in regards to results wrapped in MatrixForm[]. As in "Why doesn't mat={{1,2},{3,4}}//MatrixForm;RowReduce[mat] work?". Same issue. – Daniel Lichtblau Nov 26 '15 at 15:28
  • @DanielLichtblau Is there a reason BaseForm[expression,10] should be treated differently than any other output, other than that it is being processed through a formatting function? It would be handy if there was a way to selectively disable the outer BaseForm wrapper when using the default case (perhaps through a property?). – Dustin Darcy Nov 26 '15 at 17:58
  • 1
    I don't know why BaseForm[expr,10] formats without the "sub_10" part in contrast to other bases. But that's in the formatting handler. The fact is XXXForm wrappers do not go away, so functions that see them in input need to be prepared for handling them. – Daniel Lichtblau Nov 26 '15 at 18:25
  • If the formatting handler recognizes that it's using the default case, it seems there should also be a way to simply treat the XXXForm as a pass through. Thank you for your insight Daniel. – Dustin Darcy Nov 26 '15 at 19:06
  • How an expression is formatted does not in any way affect the expression. In particular, the expression BaseForm[number,10] does not become transformed to number. It might be an oversimplification, but it would be useful to regard evaluation and formatting as separate things. Maybe regard formatting as a side effect. – Daniel Lichtblau Nov 29 '15 at 17:03

2 Answers2

5

You can define a customized BaseForm:

(* Solution 1 *)    
baseForm[num_, n_] := BaseForm[num //. BaseForm[a_, _] :> a, n]
(* Solution 2, may be dangerous *)
baseForm[num_, n_] := BaseForm[Block[{BaseForm = # &}, num], n]

But personally I think keeping BaseForm out of the function definition is better. If you feel it cumbersome to type BaseForm from time to time, just define a shorter name for it:

b[n_] := BaseForm[#, n] &
5 // b@4

enter image description here


Update:

The following solution should handle Add[1, 2] + Add[3, 4]:

(* Solution 3 *)
Clear@baseForm

baseForm /: (c : Except[SetDelayed | baseForm])[d___, baseForm[a_, b_], e___] := 
  baseForm[c[d, a, e], b]
baseForm /: MakeBoxes[baseForm[a_, b_], fmt_] := ToBoxes@BaseForm[a, b]

Update 2:

(* Solution 4 *)
baseForm[num_, n_] := ($PrePrint = BaseForm[#, n] &; num)

This solution should be the most robust and sufficient for so long.

xzczd
  • 65,995
  • 9
  • 163
  • 468
  • Clever, I like it! Both solutions solve the nesting problem, but performing an operation like: Add[1,2]+Add[3,4] results in 3+7 instead of 10 due to the default value f_:10 in Add[a_, b_, f_:10]. So close! – Dustin Darcy Nov 26 '15 at 10:55
  • @DustinDarcy See my edit. – xzczd Nov 26 '15 at 12:54
  • That's one crazy transform! It works, but when the situation gets a bit more hairy (ex. Sum[x, {x, 0, Add[1, 2]}]) it generates: Sum::vloc: "The variable {x,0,3} cannot be localized so that it can be assigned to numerical values." I think it's best to write this one off as being more trouble than it's worth. Your answer definitely wins though and is probably as close as we're going to get without a rewrite of how BaseForm[] treats the default case. Thank you for all your effort. – Dustin Darcy Nov 27 '15 at 03:03
  • 1
    @DustinDarcy Thanks for accepting. As to the new sample, it seems that baseForm failed to escape from Sum. This may be because Sum has used some unusual rule for summation. (Here is an evidence for the unusualness of Sum.) A quick fix I can think out is to define a customized Sum: sum[f_, {x_, a_, b_}] := Sum[f, {x, #, #2}] &[a, b]. This is probably not the only edge case though 囧. – xzczd Nov 27 '15 at 05:57
  • @DustinDarcy Have a look at my update 2, this solution should be quite robust. The only trouble is that $PrePrint won't be automatically cleared but it's not a big deal. When you don't want to see the BaseForm of the output anymore, just $PrePrint=. – xzczd Nov 27 '15 at 07:36
  • After J.M's recommendation I created something a bit similar using the $PrePrint hack, but there is no way automatically unset $PrePrint before the return. As mentioned earlier the notebook has a large number of calls to BaseForm[]. So having to manually unset $PrePrint would almost be as cumbersome as constantly writing out: BaseForm[expression, X]. Thanks for keeping at it though, xzczd! – Dustin Darcy Nov 27 '15 at 08:36
  • @DustinDarcy Maybe I haven't understand the situation correctly, but are you awaring that $PrePrint doesn't need to be unset before being set to a new function? : http://i.stack.imgur.com/5YaFr.png – xzczd Nov 27 '15 at 08:58
  • Yep. The notebook I'm working with looks more like this: BaseForm[expression,2]; expression; BaseForm[expression,8]; expression; So after each BaseForm expression I'd have to clear $PrePrint to return the output back to the normal base-10; or every normal line in the notebook that doesn't use BaseForm[] would have to be converted to use the wrapper. – Dustin Darcy Nov 27 '15 at 20:30
1

One solution that works when the value isn't being stored if it's not base-10 is to use a wrapper to conditionally apply the BaseForm.

baseForm[e_,n_]:=If[n==10,e,BaseForm[e,n]]

This fixes the nesting problem because BaseForm[] is only applied in the last call of the execution stack. It is too bad BaseForm[] doesn't do this by default.

Dustin Darcy
  • 107
  • 6
  • Oh… so there doesn't exist something like a = Add[2, 2, 3](* line break *) a + a in your notebook? – xzczd Nov 28 '15 at 02:53
  • Good observation, most of the BaseForm[expression, _not_ 10] operations output unstored values which are later referenced by the line number or the result rather than variable to point out changes that are happening. That is definitely a limitation of this approach. Time to go back through the notebook to double check what it might have messed up. – Dustin Darcy Nov 28 '15 at 04:39