Chapter 6, Exercise 5, MEANS

William Fish's picture

I would like to see a solution to Exercise 5 at the end of Chapter 6 in the 2007 edition of the Maple 11 Introductory Programming Guide. It's on page 255. My humble attempt is:

View 4937_Chapter6Exercise5.mw on MapleNet or Download 4937_Chapter6Exercise5.mw
View file details

I am asking this question because in the past the answers have been way cool, magic happens! I hope that this doesn't discourage any answers.

Comments

JacquesC's picture

Means, 2 ways

I guess this is cheating? Means := proc(L::list(list)) map(Statistics[Mean], L) end;

Otherwise I guess I would write Means := proc(L::list(list)) map(proc(L1) local i; add(i,i=L1)/nops(L1) end proc, L) end proc;

Note how my code is as close to the mathematical definition as I can make it -- and eschews CSisms like explicit loops. My bias: you are well on your way to 'get' Maple when you cringe whenever you are obliged to write an explicit loop.

leaner and meaner

How 'bout

Means3 := curry(map,`+`/nops):

I wouldn't recommend that for production work, but it is nice and short and does the job.

JacquesC's picture

Gorgeous!

I am jealous. I wish I had thought of that.

Arrgghh

Alas, it looks better than it works. I had tested it, but with several others, at once, and didn't notice that it didn't actually work. `+` doesn't work on a list, but rather on a sequence. It can be salvaged with

Means3 := curry(map,`+`@op/nops);

but that isn't as nice looking.

Nothing should be considered

Answers 2 and 3 should be modified to work for an empty list. For #2, this is simple. For #3, the beauty is lessened, unfortunately.

types

Which was why I suggested it wasn't appropriate for "production". That and the complete lack of type checking. I haven't read the actual problem statement, does it define what to do with an empty list? Note, also that the empty list [] type-matches list(list), so that must be dealt with as well. Possibly the input arguments should be declared as list(list(algebraic)).

William Fish's picture

'getting' Maple

Thank you, thank you, thank you, ...

Cheating, what is cheating?

I assume that CSisms refers to the C programming language and not Caesars, the emperors of Rome. CSisms is a cool word. Why isn't the word "Cisms"?

http://en.wikipedia.org/wiki/C_(programming_language)

I hope to 'get' Maple one day.

This: "Means3 := curry(map,`+`@op/nops);" is magic. I have no idea how it works. Finding out will entertain me for some time. I've read the "curry" help page once but once isn't enough. I'll have to read it a few more times. That Morse Code stuff at the end of the "curry" help page looks cool too.

I wrote "local i::integer,positive,"... I don't think that that does what I wanted. How do I declare a local variable to be a positive integer?

Is declaring a local integer variable "i" a CSism?

JacquesC's picture

CS = Computer Science

So CSisms means issues that only matter from a computer science point-of-view. Loops are an operational concept which is forced upon us by sequential computers, not an issue that should matter when solving mathematical problems.

Declaring your intent (that i is a positive integer) is a mathematical concern. Nicely, Maple (finally!) lets you do this.

BTW i::And(integer, positive) is one way to say that.

Positive integers and others

Please see ?posint

J. Tarr

William Fish's picture

passed vs _passed

posint does just what I want.

i::And(integer, positive) looks like it could be very useful in cases where the equivalent (posint) doesn't exist. i.e.: posreal::And(numeric,positive)

Does assume() have an application in this area? I've tried to use assume() several times and it didn't make-it-happen for me.

When should _passed (args) be used rather than something like the passed that I used?

Can _passed be typed? Should it be typed? Would typing _passed be a good thing?

_passed

Typing _passed makes no sense and cannot be done. _passed, which is equivalent to args (an older name for the same thing), is a special symbol assigned the sequence of arguments actually passed to a procedure.

The expression _passed(args) is equivalent to args(args). I doubt there is ever a practical use for that construction.

William Fish's picture

Means3 := curry(map,`+`@op/nops);

I've been looking at the subject line for a while now.

In that line nops causes the sums to be divided by the number of terms in each sum to produce the desired means. I would like to know if there is a way to divide the sums by the number of sums, lists or columns. The input is a list of lists. Is there a way to divide the sums by the number of lists rather than by the number of terms in each sum?

I happy that the subject line doesn't work this way but, I don't understand why it doesn't. Does Maple know that that would be silly and means are obviously desired? Naa...

No one would ever want to do this except for someone who wanted to be able to get Maple to do whatever he or she wanted no mater how whimsical.

puzzle

Rather than tell you the answer, I'll give you a hint. You can modify

curry(map,`+`@op/nops);

to return a "procedure" that divides by the number of lists, rather than the number of items in each list, by shifting the position of one character. That is, move one of the characters in the code.

Robert Israel's picture

Means3

"Does Maple know that that would be silly and means are obviously desired?" No, emphatically no! Never forget that Maple, despite all its power, has absolutely no intelligence. It has no idea what you want to do, and it is just doing what you tell it to do. In those cases where it does try to guess what you meant rather than what you wrote (e.g. in parts of the Standard GUI) the results are often disappointing.

You have to take a look at what Means3 actually is, and work out what it does. The "curry" is just a convenient way to produce a procedure without using -> or proc. For the understanding of Means3, it's probably better to look at the direct definition.

> Means3 := () -> map((`+` @ op)/nops, args);

So here's what happens when Means3 is given a list of lists:

> Means3([[a,b],[c,d,e]]);

The "map" applies its first operand, the function (`+`@op)/nops, to each operand of the argument [[a,b],[c,d,e]], i.e. to each of the lists [a,b] and [c,d,e]. `+`@op adds the members of the list, since "op" turns the list into a sequence and `+` does the addition. "nops" takes the number of operands of the list. Thus (`+`@op)/nops adds the members of a list and divides by the number of operands.

If, let's say, you wanted to divide by the number of lists, that number is "nops" of the top-level list. So you don't want that part to be mapped. You could use

> Means4 := curry(map,`+`@op)/nops;

or more directly

> Means4 := () -> map(`+`@op,args)/nops(args);

multiplication distributes over lists

To the careful observer, there is at least one piece of magic remaining in Robert's explanation. That is, Maple automatically distributes multiplication (and division) by numeric values over lists. For example,

3*[a,b,c];
                  [3*a, 3*b, 3*c]

This does not occur for elements that are not of type numeric, nor if any term (besides the list) is nonnumeric, unless one of the elements is equal to zero. Nor does it work over sets.

3*I*[a,b,c];
                 3*I*[a,b,c];
3*a*I*[a,b,c]*0;
                    a*[0,0,0]

In the first assignment to Mean4, curry(map,`+`@op)/nops, nops returns an integer which is then distributed over the list(list) structure.

William Fish's picture

"curry" is just ...

Your understanding of Maple is so much better than mine that you will never and can never know how much that helped. Thank you all, Robert Israel in particular.

Although the magic is no longer so black, this:

Means3 := () -> map((`+` @ op)/nops, args);

still seems miraculous because the function, (`+`@op)/nops, needs and gets the elements of args, the lists not the list of lists, in two places.

The map help page says the equivalent of:

"map(fcn, expr) executes fcn(elem) for each element ..."

if doesn't say:

"map(fcn, expr) executes fcn(elem, elem) for each element ..."

Also, in the case of the test data that I used ([[1,2],[1,2,3]]) is (1+2)/2 computed or is 1/2 + 2/2 computed? From the output that results from printlevel:=1000: I think that (1+2)/2 is computed. So, is division being distributed over the elements of the list?

applying arguments to expressions.

The reason it works is that Maple can apply arguments to an algebraic expression. See ?evalapply for details. For example,

(3*f+a/b)(1,2);
                    3*f(1,2) + a(1,2)/b(1,2)

Division by a numeric quantity is distributed over a list. See my previous response in this thread.

William Fish's picture

Distributed Division

In the following worksheet printlevel:=1000: shows that in the last expression 3 and 6 are computed. So in that case, which is what curry(map, (`@`(`+`, op))/nops) yields, the division is not distributed.

View 4937_Chapter6Exercise5a.mw on MapleNet or Download 4937_Chapter6Exercise5a.mw
View file details

yeahbut

Yes, but that is because there is no list to distribute over. `+`@op([1,2,3] evalutes to 6, which is then divided by 3 (nops([1,2,3]).

William Fish's picture

OK

Thank you.

It's hard for me to tell what's going on even with the printlevel set to 1000. Apparently I did see the values 3 and 6 evaluated.

It has been said about lisp practitioners, probably by C or assembly programmers, that they “know the value of everything and the cost of nothing.” Computers are so fast and numerous these days that no one would say that anymore.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
}