Carl Love

Carl Love

28065 Reputation

25 Badges

13 years, 25 days
Himself
Wayland, Massachusetts, United States
My name was formerly Carl Devore.

MaplePrimes Activity


These are answers submitted by Carl Love

Here's an example:

#List of desired numeric values of tickmarks (they don't need to be exact):
NTM:= [1e-5]:
 
plot(
   0, view= [(0..2e-5) $ 2], #just dummies for this example
   axis[2]= [ #axis[2] is the vertical axis
      tickmarks= [
         seq(
            #format is "actual numeric value" = "what you want printed"
            y= typeset(`1O`^nprintf("%d", ilog10(y))),
            y= NTM
         )
      ]
   ]
);

Details: ilog10(y) returns, as an exact integer value, the integer part of the base-10 logarithm of abs(y); in this case, that's -5. Then I used nprintf to to turn -5 into the string[*1] `-5`. This hides that minus sign inside the quotes so that the two-dimensional processor typeset won't interpret it as something to put in the denominator.

By varying the above, almost anything that can be prettyprinted in a Maple worksheet can be similarly prettyprinted as a tickmark. You can even change the font, size, etc. Note that nprintf can take pretty much anything and turn it into a one-dimensional string of characters (even Chinese characters, for example, if you know their Unicodes); typeset handles the two-dimensional aspects, and is much more fussy and limited. More-complicated 2D cases require using the mostly undocumented Typesetting package.

Cases where you don't have foreknowledge of the precise numeric values of the y-axis tickmarks are much more difficult to handle. I've thought for years about that, but haven't come up with a general solution.

[*1]In Maple-lingo, what I'm calling a "string" is properly called a "symbol".

The amazing thing about Maple is that in one short line of code, I can write a meta-procedure that broadly generalizes your assignment; and in a second short line, I can invoke that procedure to produce the procedure that directly fulfills your assignment:

restart:
MetaOp1:= (gen, n::nonnegint, pred, EA)-> ()-> EA(select(pred, ['gen()'$n])):
MyOp1:= MetaOp1(rand(1..100), 20, x-> x::even, x-> (S-> (S, S/nops(x)))(add(x))):

I'll show a test invocation of MyOp1 in a moment. Here, gen is a procedure that generates the numbers, n is the number of numbers to generate, pred is a predicate (such as Is x even?), and EA is the "external accountant" (by which I assume that your teacher  means the procedure that does the sum and average).

That doesn't use a for-do loop, so here's a meta-procedure that does:

MetaOp2:= proc(gen, n::nonnegint, pred, EA)
   proc()
   local R:= table(), k, r;
      for k to n do if pred((r:= gen())) then R[k]:= r fi od;
      EA([entries(R, 'nolist')])
   end proc
end proc: 
MyOp2:= MetaOp2(rand(1..100), 20, x-> x::even, x-> (S-> (S, S/nops(x)))(add(x))):

The arguments and invocation of MetaOp2 are exactly the same as those of MetaOp1. Now, the usage examples: In the following, randomize is used the get the same random sequence for both tests, but I've done it in such a way that you'll get a different random sequence each time that you run the pair.

randomize((r:= randomize())):
MyOp1();
                                 604
                            604, ---
                                 15 
randomize(r):
MyOp2();
                                 604
                            604, ---
                                 15 

I hope that you'll take this as instruction showing the broad possibilities of Maple and come up with your own solution to your homework.

It's very important for any Maple programmer to learn the trick (shown in MetaOp2) of accumulating results in a table within a do loop and then returning the entries or indices or both of that table. I'd probably fail a student who hadn't learned that by the end of the course. It's not always necessary to explicitly declare a table; for example, the and L1 in Kitonum's Answer are tables. Never accumulate partial results in a list, set, or sequence; that's extremely inefficient.

I find this solution method to be much simpler than Kitonum's:

restart;
A:= <1, 4; 5, 1>;
x0:= <2, 1>; #initial condition
M:= Matrix(DETools:-matrixDE(A, t)[1]); #convert to Matrix
x:= M.(eval(M, t=0)^(-1).x0);

In particular, it avoids the need of the variables C1 and C2.

Note that I included an extra set of parentheses in the final line. Since Matrix multiplication is associative, these are mathematically superfluous. However, by using them to change the order of operation, the result is substantially simplified.

Use

subsindets(p, patlist(0, anything), ()-> ());

This will remove any list (regardless of length) whose first element is 0. Of course, this is done without disturbing any other part of the structure.

I think that I can anticipate your next several questions, and I need to be away from the Internet for a few days. So, the code below will give you something to think about for a few weeks at the very least (and that would be if you study Maple all day, every day). But this code does seem to me to be the direction that you're heading.

The procedure below takes an unevaluated expression that can be evaluated at different levels (which I call a "cascade of evaluations" or "evaluation chain" or "evaluiation tree") and saves it as a vector of strings that can be used to reconstruct the entire cascade, even in a different Maple session, even on a different computer.

This uses some of the deepest non-mathematical concepts of Maple. It is a testament to the power and beauty of Maple that this can be done in such a short procedure.
 

restart:

(* Procedure SaveNameCascade: Takes a name (which could be the start of an
"evaluation cascade") and returns a Vector of strings which can be parsed
(even in another Maple session!) to reconstruct the cascade.

Some caveats:
1. Reconstructed names are always global, even if the originals weren't. There's
no way around this.

2. It's possible that there are assigned names that only appear in indices that
my indets commands below will not find. If there's demand for it, I can handle
that situation with more-complex (and recursive) indets commands.

3. It's possible to create expressions whose evaluation causes changes to the
assigned values of some names in the cascade; in other words, self-modifying
expressions. Indeed, it's quite easy to intentionally do that for the purpose of
creating bad examples. Obviously, the results of applying this procedure to such
expressions may be nonsense.

4. It's even possible to create expressions whose partial evaluation contains a
reference to the unevaluated original. Obviously, attempting to fully evaluate
these results in an infinite loop.
*)


SaveNameCascade:= proc(t::uneval)
option `Author: Carl Love <carl.j.love@gmail.com> 4-Oct-2018`;
local j:= 0, k, n, e:= t, v:= eval(t), R:= Vector(), Match;
   for k while e <> v do
      for n in
         indets(e, 'And'('name', 'Not'('indexed'), 'satisfies'(n-> n <> eval(n))))
         union
         map2(
            op, 0,
            indets(
               e,
               'And'(
                  {'function', 'indexed'},
                  'satisfies'(f-> op(0,f) <> eval(op(0,f)))
               )
            )
         )
      do
         if not assigned(Match[eval(n,1)])  then
            Match[eval(n,1)]:= ();
            j:= j+1;
            R(j):= cat(sprintf("assign('%a' = '%Q", n, eval(n,2)), "')")
         fi
      od;  
      e:= eval(e,2)
   od;
   R
end proc:
            

#Example #1:
A:= B:  B:= C:  C:= 7: #Construct a (single-path) evaluation cascade.
seq(eval('A',k), k= 1..4); #View it.

A, B, C, 7

R:= SaveNameCascade(A); #Save the cascade as a Vector of strings.

Vector(3, {(1) = "assign('A' = 'B')", (2) = "assign('B' = 'C')", (3) = "assign('C' = '7')"})

 A:= 'A':  B:= 'B':  C:= 'C': #Delete the cascade (and all sub-cascades).
eval~([A, B, C]); #Verify that it's deleted.

[A, B, C]

parse~(R, statement): #Rebuild the cascade from saved strings.
seq(eval('A',k), k= 1..numelems(R)+1); #View it.

A, B, C, 7

#Example #2: A more-intricate example of a branched-tree cascade, including
#an Array, reconstructed in another Maple session:
L:= [x[1,2], x[2,1]];  x:= Array(1..2, 1..2, (i,j)-> 2^i*3^j);
seq(eval('L',k), k= 1..3); #View it.

L := [x[1, 2], x[2, 1]]

Array(%id = 18446746286613268470)

L, [x[1, 2], x[2, 1]], [18, 12]

MySavedTree:= SaveNameCascade(L);

Vector(2, {(1) = "assign('L' = '[x[1,2], x[2,1]]')", (2) = "assign('x' = 'Array(1..2, 1..2, [[6,18],[12,36]])')"})

#You'll need to change this directory name to something appropriate
#for your computer:
currentdir("C:/Users/CarlJ/Desktop"):

save MySavedTree, "SavedTree.mpl";


#Start a new Maple session:

restart:

currentdir("C:/Users/CarlJ/Desktop"):

read "SavedTree.mpl";

Vector(2, {(1) = "assign('L' = '[x[1,2], x[2,1]]')", (2) = "assign('x' = 'Array(1..2, 1..2, [[6,18],[12,36]])')"})

parse~(MySavedTree, statement):
seq(eval('L', k), k= 1..3);

L, [x[1, 2], x[2, 1]], [18, 12]

 


 

Download NameCascades.mw

Like this:

algcurves[plot_real_curve]((x^2+y^2+2*y)^2 - 4*(x^2+y^2), x, y);

This only works for polynomials in two variables, but for such curves, this command gives much better results than other plotting commands.

Like this (is the simplest[1] way):

MaxAbs:= L-> max(abs~(L));
and its usage would be
MaxAbs(L1);

You can (and probably should) add some type protection, like this (just an example of many possibilities):

MaxAbs:= (L::list(complexcons))-> max(abs~(L));


[1]In some simple cases, such as this one, it is possible to create functions without using the arrow by building them from existing functions with functional operators such as @ (composition) and (elementwise). So,

MaxAbs:= max@abs~;

When this is possible, it is often (but not always) more efficient than using the arrow. 

The only official help for it that I can find is on page ?type,structure:

  • The type specfunc(t, n) matches the function n with 0 or more arguments of type t.  Thus, type(f, specfunc(t, n)) is equivalent to the logical conjunction type(f, function) and op(0,f) = n and type([op(f)], list(t)). Instead of a single name n, a set of names may be passed as the second parameter to the specfunc type constructor.  In this case, an expression matches if it is a function call with any of the names in the set as the 0-th operand. Thus, type(f,specfunc(t,{n1,n2,...,nk})) is equivalent to type(f,specfunc(t,n1)) or type(f,specfunc(t, n2)) or ... or type(f,specfunc(t,nk)).

That's pretty dense material, but if you have any specific questions about it, I have an expert-level understanding of it, so fire away.

The only thing that I can imagine that you're failing to see about it is that all arguments of the function must be of the specified type(s). So, consider your F: Because of the j, there is no occurence of u with all arguments of type `+`.

If you're only interested in type-checking u's first argument, we can work on that; there are a few options. Also note that specfunc does nothing to control the number of arguments; u() will pass through all of your typespecs, and the transformer will then fail because it requires op(1, ...).

And I suppose that you already know that {type1, type2} is the union of those types.

Unrelated issue: You could (I'm not saying should) replace your transformer that uses subsop with

f-> applyop(`+`, 1, f, 1);

There's no efficiency benefit (as of Maple 2018) to doing this, since applyop simply translates it back to what you had with subsop, which you can see via showstat(applyop). I just think that it's a bit cleaner with applyop.

 


 

There are two ways to get the name, strictly speaking: evaln(x) or 'x'. Those are forward single quotes, and they're known in Maple as unevaluation quotes or delayed evaluation quotes. They're very commonly used, but evaln is not. There's a slight distinction between the two: The quotes can be used on anything, but evaln requires an underlying name.

The way that you posted (using double quotes) indicates that you might want the name as a string. In that case, you still need to use one of the options from the first paragraph, and then convert to a string:

convert('x', 'string');
or
convert(evaln(x), 'string');

The quotes on string are not strictly necessary in this case. I just wanted to show you how unevaluation quotes are commonly used. They protect against the possibility that you've assigned a value to string (probably unintentionally).

I'll suppose that the above set is named Sols. Then all you need to do is eval with respect to Sols and then use assign, like this:

Sols:= {R = 7.339698158, S = 2.378491488, W = 2.512047349}:
assign(eval([X=R, Y=S], Sols));

The assign command displays no output, but if you look at X and Y, you'll see that they're the desired numbers.

The commands printf and print cause stuff to be displayed on your screen. In a trivial sense, this could be considered "output", but, as you've noted, it's not usable. Ordinarily, this is a common rank-newbie error, but considering the way that the exercise's instructions are given on RosettaCode, the author made a reasonable choice. A procedure's true output, or return value, is set by a return statement, or more usually as the last expression evaluated. Here's a procedure that does what you want:

splitChange:= proc(str::string) 
local start, i:= 1, len:= StringTools:-Length(str), R:= table();
   while (start:= i) <= len do
      for i from start to len do until str[i] <> str[start]; 
      R[start]:= str[start..i-1]
   end do;
   [entries(R, 'indexorder', 'nolist')]
end proc:

 

That's a very interesting discovery. The strange thing to me is not that the bond is lost in the second case, but that the bond exists in the first case. If the bond continued to exist in the second case, that would be a mystery akin to quantum entanglement.[*1]

I notice that you're saving to Maple-internal-format .m files. Apparently, that format retains the information that items share a memory address. If you change to a plain-text format by using any filename other than *.m, then the strange bond disappears.

But the issue has absolutely nothing to do with tables or eval, so it'd be helpful if you retitled this Question. Here's an illustration using simple name variables:

restart:
A:= B: B:= x:
currentdir("C:/Users/CarlJ/Desktop"):
save B, A, "FC.m"; #Note: B, A; not A, B!
restart:
currentdir("C:/Users/CarlJ/Desktop"):
read("FC.m");
B:= y:
A;
                               y

[*1]Come to think of it, suppose that this were being run on two quantum computers. The files containing A and B, respectively, are created on computer Q1. File B is sent to computer Q2, and it's read there. Is it possible that the bond could be maintained?

Change cat(elemt) to parse(elemt). The help page does not say that the element name / symbol can be a string, although I'd agree that a string should be allowed.

f:= (t,y)-> -y + t*y^(1/2);
diff(f(t, y(t)), t);

It simply can't be done for an Array (note the distinction with array), and I don't see why you'd want to do it. An Array's entries always have a value. That value may be NULLundefined, etc., but those are still values.

First 161 162 163 164 165 166 167 Last Page 163 of 395