5
$\begingroup$

I have a list of functions

func[x_]={1,Cos[x],Sin[x]}

(for example) and I want to apply each function to a list of values

xval={a,b,c,d}

obtaining a list of tuples. The problem is that the constant function will not evaluate to a tuple (it will remain as a scalar)

func[xval] = {1, {0, -1, 0, 1}, {1, 0, -1, 0}}

Is there a way to force the creation of a tuple (with the function value) of the same size as xval, if I don't know what are the functions in func (if there is or not a constant function in it)?

$\endgroup$
5
  • 2
    $\begingroup$ You can use pure functions: funcs = {# , Cos[#] , Sin[#] }& $\endgroup$ Oct 27, 2016 at 10:14
  • $\begingroup$ @MariusLadegårdMeyer Won't that give x in the first component, not a list of copies of the constant? $\endgroup$
    – Michael E2
    Oct 27, 2016 at 12:03
  • 1
    $\begingroup$ @MichaelE2, sorry, I changed it and forgot to change it back again :p The correct thing is funcs = {1 + 0 # , Cos[#] , Sin[#] }&. $\endgroup$ Oct 27, 2016 at 14:14
  • $\begingroup$ @MariusLadegårdMeyer Or just 1& ({1, Cos@#, Sin@#}&). $\endgroup$
    – The Vee
    Oct 27, 2016 at 19:53
  • $\begingroup$ you could also add one more definition to your function: func[t:{__}]:=func/@t $\endgroup$ Oct 30, 2016 at 0:01

7 Answers 7

6
$\begingroup$

The reason why it works with Cos and Sin is that those functions are Listable. So just make a constant function that is Listable, too:

func[x_]:={Function[y, 1, Listable][x],Cos[x],Sin[x]}
xval={a, b, c, d}
func[xval]
(*
==> {{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]},
     {Sin[a], Sin[b], Sin[c], Sin[d]}}
*)

Note that here it is important that you use := to define func, otherwise it gets evaluated already at definition time, resulting in a single 1 again.

If you need it often, you can define a function to generate a listable constant function:

const[x_] := Function[y, x, Listable]

Then you can write:

func2[x_] := { const[1][x], Cos[x], Sin[x] }
func2[xval]
(*
==> {{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]}, 
     {Sin[a], Sin[b], Sin[c], Sin[d]}}
*)
$\endgroup$
1
  • $\begingroup$ I really liked the simplicity of your answer, and the function definition const[x_] you showed. It works very well (and solves @Mr.Wizard's problem with Lists inside the argument list). Thank you! $\endgroup$
    – malu
    Nov 6, 2016 at 20:08
11
$\begingroup$

First: you should probably be defining func using SetDelayed rather than Set.

The behavior you want is closely related to the concept of Listable functions in Mathematica. It is this attribute that makes Sin and Cos behave as you observe. If you add the Listable attribute to func either directly (using SetAttribute, i.e. SetAttribute[func, Listable]) or by way of a container Function you get this:

f2 = Function[xx, func[xx], Listable];

f2[{a, b, c, d}]
{{1, Cos[a], Sin[a]},
 {1, Cos[b], Sin[b]},
 {1, Cos[c], Sin[c]},
 {1, Cos[d], Sin[d]}}

You may Transpose this output to achieve the form you desire:

f2[{a, b, c, d}]\[Transpose]
{{1, 1, 1, 1},
 {Cos[a], Cos[b], Cos[c], Cos[d]},
 {Sin[a], Sin[b], Sin[c], Sin[d]}}

Another approach may be to Map the function over the list manually, then transpose:

Map[func, {a, b, c, d}]\[Transpose]    (* same output as above *)

A more targeted approach

Reconsidering this problem perhaps what you want can be attained with post-processing the existing output of func. I tried to keep the method above as general as possible, however since you did specify "a vector of values" rather than an arbitrary List-based structure there is a simpler method using Thread. Consider the behavior:

Thread[{1, {a, b, c, d}}]
{{1, a}, {1, b}, {1, c}, {1, d}}

This allows us:

func[{a, b, c, d}] // Thread // Transpose
{{1, 1, 1, 1},
 {Cos[a], Cos[b], Cos[c], Cos[d]},
 {Sin[a], Sin[b], Sin[c], Sin[d]}}

Caveat: if the constant term in the original function is itself a List this fails:

func2[x_] := {{1}, Cos[x], Sin[x]};

func2[{a, b, c, d}] // Thread // Transpose

Thread::tdlen: Objects of unequal length in {{1},{Cos[a],Cos[b],Cos[c],Cos[d]},{Sin[a],Sin[b],Sin[c],Sin[d]}} cannot be combined. >>

$\endgroup$
4
$\begingroup$

You can Map the function over the list and Transpose the result:

func[x_] := {1,Cos[x],Sin[x]};
xval = {a, b, c, d};
Transpose[func /@ xval]

Result:

{
  {1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]},
  {Sin[a], Sin[b], Sin[c], Sin[d]}
}

You can even overload the function if you want it to do this on any list automatically:

func[x : Except[_List]] := {1,Cos[x],Sin[x]};
func[list_List] := Transpose[func /@ list];
$\endgroup$
3
$\begingroup$

I've been interested in a vectorized constant function that, like built-in mathematical functions, evaluates packed arrays to packed arrays. Here's a way, but you have to manually implement listability for non-packed arrays and other lists.

constFN[k_][x_?ArrayQ] := ConstantArray[k, Dimensions[x]];
constFN[k_][x_List] := constFN[k] /@ x;
constFN[k_][x_?NumericQ] := k;

Examples:

constFN[2][{2, 3, {4}}]                (* listability *)
(*  {2, 2, {2}}  *)

constFN[2.][E]
(*  2.  *)

constFN[2.][x]                         (* produces a packed array *)
% /. x -> RandomReal[1, {2, 3}]
% // Developer`PackedArrayQ
(*
  constFN[2.][x]
  {{2., 2., 2.}, {2., 2., 2.}}
  True
*)

ifn = Interpolation@ Table[{{t}, {Cos[t], Sin[t]}}, {t, 0., 2., 1./128}];
constFN[Tan[x]][ifn[x]] /. x -> Pi/6   (* constant symbolic expression *)
(* {1/Sqrt[3], 1/Sqrt[3]}  *)

constFN[Range[4]][x]                   (* multiples of a vector constant *)
% /. x -> RandomReal[1, 3]
% // Developer`PackedArrayQ
(*
  constFN[{1, 2, 3, 4}][x]
  {{1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4}}
  True
*)

Notes: Using Listable unpacks packed arrays, which is why I did it manually.

ClearAll[fUP];
SetAttributes[fUP, Listable];
fUP[k_, x_] := k;

On["Packing"];
fUP[2., RandomReal[1, 3]] // Developer`PackedArrayQ
Off["Packing"]

Developer`FromPackedArray::unpack: Unpacking array in call to HoldForm.

Developer`FromPackedArray::punpack: Unpacking array with dimensions {3} in call to fUP.

(*  False  *)

Note also that constFN[] replaces the packed arrays in any sort of (nested) list of packed arrays with constant packed arrays. One could also use SparseArray instead of packed arrays. Normal converts these to packed arrays when k is a machine Integer or Real. Just replace the ConstantArray definition with the following:

constFN[k_][x_?ArrayQ] := SparseArray[{}, Dimensions[x], k];
$\endgroup$
3
$\begingroup$

Also

Through[{1 & /@ # &, Cos, Sin}@xval]

{{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]}, {Sin[a], Sin[b], Sin[c], Sin[d]}}

Or, if func[x_]:={1, Cos[x],Sin[x]} is already defined,

Through[(Function[y, #, Listable] & /@ func[y])@xval]

{{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]}, {Sin[a], Sin[b], Sin[c], Sin[d]}}

$\endgroup$
2
$\begingroup$

You can use ConstantArray, but you have to specify your variable.

Clear[func2]

func2[x_List] := {ConstantArray[1, Length[x]], Cos[x], Sin[x]}
func2[x_] := {1, Cos[x], Sin[x]}

func2[xval]
(* {{1, 1, 1, 1}, {Cos[a], Cos[b], Cos[c], Cos[d]}, {Sin[a], Sin[b], Sin[c], Sin[d]}} *)

func2[a]
(* {1, Cos[a], Sin[a]} *)

There is also a way to do it with pure functions:

funcP := {1 + #*0, Cos[#], Sin[#]} &

You have to add 0 to your constant. I dont know if this is the best way to do this, but maybe these two approaches will help you.

$\endgroup$
2
$\begingroup$

Using Query

func = {1 &, Cos, Sin};
xval = {a, b, c, d};

Query[Transpose, func] @ xval

gives

{{1, 1, 1, 1},
 {Cos[a], Cos[b], Cos[c], Cos[d]},
 {Sin[a], Sin[b], Sin[c], Sin[d]}}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.