Showing posts with label key. Show all posts
Showing posts with label key. Show all posts

Wednesday, December 2, 2015

The Basics of Slot (#) in Pure Functions

Pure Function, as it's called, such as Function[x, x^2] that yields x2 or the beautiful shorthand, #^2&, is extremely useful in Mathematica:
  1. For one-time functions
  2. As an argument in many functional-style commands such as Map, Nest, Fold, Select, Count, etc
  3. In the extended Postfix style of programming I developed to eliminate hard-to-read nested functions
  4. In predicates including those used for argument type-checking: _?(# <= 1 &)
  5. In the general pattern-matching predicate MatchQ
  6. And the new use in Associations in version 10

Slot must be used with the pure Function operator &. They are designed to be the Head of a function:

#1&[a,b,c]

a

When you use the Postfix style just remember that Postfix causes the ampersand abbreviation for Function to wrap around an entire expression preceding it. Thus it has a low precedence of 90; here are its neighbors:

Precedence/@{Set,Postfix,Colon,Function,TimesBy,Rule}

{40.,70.,80.,90.,100.,120.}

It can be confusing if there are nested used of Function, but using that principle, you can figure it out.

Important: What trips up beginners is using Slot with Mathematica's universal container, List. This doesn't work:

#1&{a,b,c}

{a (#1&),b (#1&),c (#1&)}

There are several solutions. Using Apply (@@) will return a Sequence:

args=##&@@{a,b,c,d}

Sequence[a,b,c,d]

A Sequence is a list designed to be fed to any function:

func@args

func[a,b,c,d]

You can wrap SlotSequence in List so it returns a List:

{##}&[a,b,c,d]

{a,b,c,d}

Here are typical uses with Function as the Head or using Apply. Note that you cannot use negative position numbers with Slot or SlotSequence since that would conflict with using Subtract in pure Functions (like #-2&).

"First" (the default position argument for Slot is 1 so you don't need to specify it with "#1")

#&@@{a,b,c}

a

Return the first two arguments:

{#,#2}&@@{1,2,3,4}

{1,2}

Return all arguments:  ## is SlotSequence, which returns a Sequence, which is designed to be given to any function as its argument list.

##&[a,b,c,d]

Sequence[a, b, c, d]

{##}&[a,b,c,d]

{a, b, c, d}

##&@@@[a,b,c,d]

{a, b, c, d}

Return the first and fourth arguments:

{#,#4}&@@{1,2,3,4}

{1,4}

"Rest": Return the 2nd argument to the last:

{##2}&[a,b,c,d]


Sequence[b,c,d]

As of version 10, there is an interesting new usage for Slot with Associations. Slot suffixed with any Key (name) of an Association will automatically return the Value associated with the Key.

cityPopulationAssociation=Association["Tokyo"->"37 million","Jakarta"->"26 million","Seoul"->"17 million","Delhi"->"22 million","Shanghai"->"21 million"];

#Delhi&@cityPopulationAssociation

22 million

Wednesday, July 8, 2015

New in Mathematica 10: Associations as General-Purpose Associative Arrays

Associations are the most important data structure introduced in Mathematica in years. As the Guide says:

Along with lists, associations are fundamental constructs in the Wolfram Language. They associate keys with values, allowing highly efficient lookup and updating even with millions of elements. Associations provide generalizations of symbolically indexed lists, associative arrays, dictionaries, hashmaps, structs, and a variety of other powerful data structures. 

They of course will underlie databases and artificial intelligence data structures. They are intended to replace less structured expressions such as pairs in Lists or Rules. The code written for Association functions has been highly optimized for speed. I suspect that Associations are the core data structure of the Wolfram Alpha natural language processor, which is the front end for the curated data collections in Alpha, just as the Notebook is the front end for the kernel.

So when I needed a lookup table today I decided to try Associations. Here are some simple functions that define an axon's threshold in terms of the stimulating pulse seen at the axon, according to the Lapicque equation Vth = Vrh(1+τch/pw).

(You don't need to understand these!)

absoluteRheobase@fiberDiameter_:=0.000589+0.01518 Exp[-fiberDiameter/2.3477]

chronaxie@fiberDiameter_:=102.764 +162.46744 Exp[-fiberDiameter/5.53435];

absoluteThreshold=.;absoluteThreshold[pulseWidth_,fiberDiameter_]:=Module[{},absoluteRheobase@fiberDiameter(1+chronaxie@fiberDiameter/pulseWidth)]

Now since fiber threshold is driven by their diameter and the pulse width of the stimulating pulse, we make sample tables of those. Esc + m + esc gives us the mu in microseconds and micrometers; no need to open a Palette.

pulseWidthTable50to500=Table[pw,{pw,100,500,100}] (* in μs *);

fiberDiameters1to15=Table[diameters,{diameters,5,10}] (* in μm *);

Now here is the function that creates the Associations. It took a bit of fiddling, but by making a Table of Rules in one step as should be done with good functional programming, it is then easy to Apply Association to the entire Table, simply replacing the Head, List, with Association, which converts the List of Rules into an Association. In the Table pulseWidth is the "i" iterator and fiberDiameter is the "j" iterator, and notice that we don't need to iterate numerically from 1 to Length@pulseWidthTable50to500, for example, we just directly iterate over the List itself, which is simpler.

absoluteThresholdTable=Association@@Table[Rule[{pulseWidth,fiberDiameter},absoluteThreshold[pulseWidth,fiberDiameter]],{pulseWidth,pulseWidthTable50to500},{fiberDiameter,fiberDiameters1to15}]

<|{100,5}->0.00642849,{100,6}->0.00455515,{100,7}->0.00337826,{100,8}->0.00263167,{100,9}->0.00215327,{100,10}->0.00184348,{200,5}->0.00441095,{200,6}->0.00316135,{200,7}->0.00236852,{200,8}->0.00186172,{200,9}->0.00153533,{200,10}->0.00132348,{300,5}->0.00373844,{300,6}->0.00269675,{300,7}->0.00203194,{300,8}->0.00160507,{300,9}->0.00132935,{300,10}->0.00115015,{400,5}->0.00340218,{400,6}->0.00246445,{400,7}->0.00186364,{400,8}->0.00147675,{400,9}->0.00122636,{400,10}->0.00106348,{500,5}->0.00320043,{500,6}->0.00232507,{500,7}->0.00176267,{500,8}->0.00139975,{500,9}->0.00116456,{500,10}->0.00101149|>

Here are several versions of lookup functions. The first one just uses the same syntax as we'd use to access elements of an associative array made with function@value, such as we'd construct with a memo function.

Clear@thresholdLookup; 
thresholdLookup[lookupTable_Association, pulseWidth_Integer, diameter_] := lookupTable@{pulseWidth, diameter}

absoluteThresholdTable@{100, 7}

0.00337826

Likely it's safer and more elegant to use the new Lookup function instead.

Clear@thresholdLookup2; 
thresholdLookup2[lookupTable_Association, pulseWidth_Integer, diameter_] := Lookup[lookupTable, {{pulseWidth, diameter}}]

thresholdLookup2[absoluteThresholdTable, 100, 7]

{0.00337826}

We see that using a List as the Key results in returning a List as the Value, like in solutions for equations returned by Solve, etc. So to access the Value we can use First@key to remove the List brackets, as we often do with solutions to equations.

Clear@thresholdLookup3; 
thresholdLookup3[lookupTable_Association, pulseWidth_Integer, diameter_] := Lookup[lookupTable, {{pulseWidth, diameter}}] // First

thresholdLookup3[absoluteThresholdTable, 100, 7]

0.00337826

Years ago I talked to Stephen Wolfram at an artificial life conference about artificial intelligence and his views were kind of naive. Things have changed. I expect that Mathematica and its Wolfram Language are headed inexorably toward large-scale AI applications,  approaching, and exceeding in some respects, human intelligence over the next two decades.