# Implement a zipwith function

zipwith is a functional construct that takes three arguments: one binary function and two lists of the same length, and returns a single list where each element is constructed by applying the binary function to each pair of elements from the two lists:

zipwith (a+b) [1,2,3] [4,5,6] = [5,7,9]


You are to take a black-box function which takes exactly two arguments and two non-empty arrays of the same length consisting of positive integers, and output the result of applying zipwith to this function and arrays.

For the sake of simplicity, you can assume that the black-box function will always input and output integers within your language's integer domain.

If your answer consists solely of a builtin which does this (for example Haskell's zipWith), please edit into this Community Wiki answer. You may input and output in any convenient method.

This is so the shortest code in bytes wins

### Test Cases

function, array, array -> output
a = b, [1, 2, 3], [3, 2, 1] -> [0, 1, 0]
a - b, [8, 3], [3, 8] -> [5, -5]
a² + b/2, [6, 8, 1, 3], [5, 3, 6, 2] -> [38, 65, 4, 10]
a × φ(b), [4, 2, 5, 8, 3], [7, 6, 10, 2, 1] -> [24, 4, 20, 8, 3]


The third test case uses integer (floor) division. The fourth uses the Euler Totient function

• Sandbox – caird coinheringaahing Jan 26 at 11:04
• +1 for the underhanded way to get people to implement the totient function. – Adám Jan 26 at 11:48
• @Adám Sneaky coding is the best coding :) – caird coinheringaahing Jan 26 at 11:49
• Are integers in the list limited by my language's integer domain as well? – user99151 Jan 27 at 5:53
• @2x-1 Yes. That restriction is just to allow as many languages to compete as possible – caird coinheringaahing Jan 27 at 7:04

# Haskell, 22 bytes

(.zip).(.).map.uncurry


Try it online!

Not particularly interesting, just a reimplementation of zipWith converted to pointfree form:

\f x y -> map (uncurry f) (zip x y)
\f x -> map (uncurry f) . zip x
\f x -> (map (uncurry f) .) zip x
\f -> (map (uncurry f) .) . zip
\f -> (. zip) (map (uncurry f) .)
\f -> (. zip) ((.) (map (uncurry f)))
\f -> (. zip) ((.) ((map . uncurry) f))
\f -> (. zip) ((.) . (map . uncurry) $f) \f -> (. zip) . ((.) . (map . uncurry))$ f
(. zip) . ((.) . (map . uncurry))

• Welcome to the site! Nice first answer! – Redwolf Programs Jan 27 at 0:10
• @RedwolfPrograms, thank you! Been lurking for a while now ;-) – orthocresol Jan 27 at 0:11

# Trivial builtin answers

Edit your answer into this post if it consists of a builtin which does the entire task by itself

# APL (Dyalog Unicode), 1 byte

¨


Try it online!

# Husk, 1 byte

z


Try it online!

# Jelly, 1 byte

"


Try it online!

# C#, 3 bytes

Zip


Try it online!

# Julia, 3 bytes

map


Try it online!

# Python 2, 3 bytes

map


Try it online!

# R, 3 bytes

Map


Try it online!

The canonical R built-in function is mapply (6 bytes).
However, the R documentation for Map reads "Map is a simple wrapper to mapply which does not attempt to simplify the result" (meaning that Map leaves its output as a list, rather than a vector, of results), so this seems to be a perfectly-acceptable 3-byte alternative here.

# Factor, 4 bytes

2map


Try it online!

# Raku, 4 bytes

&zip


Try it online!

# Haskell, 7 bytes

zipWith


Try it online!

# Prolog (SWI), 7 bytes

maplist


Try it online!

# MATLAB / Octave, 8 bytes

arrayfun


Try it online!

# Elm, 9 bytes

List.map2


# PHP, 9 bytes

array_map


Try it online! (PHP's array_map can take any number of array arguments)

# Wolfram Language (Mathematica), 9 bytes

MapThread


Try it online!

• Should this answer not be sorted by byte count? – Adám Jan 26 at 11:37
• @Adám If people want to edit it around to be in byte count order, they are welcome to. So far, it's just been first-come, first-serve I believe – caird coinheringaahing Jan 26 at 11:37
• Racket: docs.racket-lang.org/heresy/…. I don’t have the TIO or anything so someone else can feel free to add it; otherwise I’ll leave it here. – D. Ben Knoble Jan 26 at 22:55
• @D.BenKnoble You can use JSDoodl or ideone too (I don't know Racket, or I would have added it in). – user Jan 27 at 1:15

# C (gcc), 49 bytes

z(f,a,b,n)int*a,*b,(*f)();{for(;n--;f(a++,b++));}


Try it online!

Loops for 0-n, applying f on &a[i], &b[i]. Fairly simple.

Ungolfed signature:

void zipwith(void (*blackbox)(int *, int *), int *a, int *b, int length);


This version expects a function like this:

void func(int *a, int *b)
{
*a = *a OP *b;
}


It overwrites the contents in a.

Since this is a pretty loose interpretation of the rules, I have also provided a more strict version which acts like a normal zipwith.

# C (gcc) strict, 61 54 bytes

z(f,a,b,n)int*a,*b,(*f)();{for(;n--;*a++=f(*a,*b++));}


Try it online!

Loops for 0-n, storing the result of f on a[i], b[i] to a[i]. Fairly simple.

Degolfed signature:

void zipwith(int (*blackbox)(int, int), int *a, int *b, int length);


Thanks to @att for reminding me I don't need a separate output array, saving 7 bytes.

# Husk, 2 bytes

δm


Try it online! (Implement the function in the footer, the header passes it to the main code)

Sure, Husk has a trivial builtin solution for this (z), but here's the shortest interesting solution :)

δ is a higher order function that turns something using an unary function on elements of one list into one using a binary function on elements from two lists.

Here, we turn the map function m that applies the (unary) input function to each element of the input list into one that applies the (binary) input function to each pair of elements from the two input lists: exactly what zipWith should do!

# x86-16 machine code, 15 14 bytes

Saved 1 byte thanks to @Joshua!

AD 50 87 F3 AD 50 87 F3-FF D2 AB E2 F3 C3


Callable function.

Inputs:

• CX = lengths of both lists
• [SI] = address of first list
• [BX] = address of second list
• [DX] = address of function

Outputs towards [DI] = address of output list (buffer needs to be large enough).

Disassembly:

       LoopTop:
AD            LODSW           ; AX = [SI], SI+=2
50            PUSH    AX      ; push AX onto stack
87 F3         XCHG    SI, BX  ; swap SI and BX
AD            LODSW           ; AX = [SI], SI+=2
50            PUSH    AX      ; push AX onto stack
87 F3         XCHG    SI, BX  ; swap SI and BX
FF D2         CALL    DX      ; call function at [DX]
; the function should take two items on the stack
; and AX = the return value (stock calling convention)
AB            STOSW           ; [DI] = AX, DI+=2
E2 F3         LOOP    LoopTop ; --CX; jump to top of loop while CX >= 0


## Example run

Tested with DOS debug:

-r
AX=0000  BX=0040  CX=0003  DX=0060  SP=FFFE  BP=0000  SI=0030  DI=0050
DS=1000  ES=1000  SS=0B17  CS=1000  IP=0000   NV UP EI PL NZ NA PO NC
-u1000:0060
1000:0060 58            POP     AX
1000:0061 A37600        MOV     [0076],AX
1000:0064 58            POP     AX
1000:0065 A37800        MOV     [0078],AX
1000:0068 58            POP     AX
1000:0069 01067800      ADD     [0078],AX
1000:006D A17600        MOV     AX,[0076]
1000:0070 50            PUSH    AX
1000:0071 A17800        MOV     AX,[0078]
1000:0074 C3            RET
-t
AX=0004  BX=0030  CX=0003  DX=0060  SP=FFFF  BP=0000  SI=0042  DI=0050
DS=1000  ES=1000  SS=0B17  CS=1000  IP=0001   NV UP EI PL NZ NA PO NC
1000:0001 50            PUSH    AX
-t
AX=0004  BX=0030  CX=0003  DX=0060  SP=FFFD  BP=0000  SI=0042  DI=0050
DS=1000  ES=1000  SS=0B17  CS=1000  IP=0002   NV UP EI PL NZ NA PO NC
1000:0002 87F3          XCHG    SI,BX
...
-t
AX=0009  BX=0036  CX=0000  DX=0060  SP=FFFF  BP=0000  SI=0046  DI=0056
DS=1000  ES=1000  SS=0B17  CS=1000  IP=000D   NV UP EI PL NZ NA PE NC
1000:000D C3            RET
-d1000:0000
1000:0000  AD 50 87 F3 AD 50 87 F3-FF D2 AB E2 F3 C3 00 19   .P...P..........
1000:0010  00 00 00 00 00 00 00 20-20 20 20 20 20 20 20 20   .......
1000:0020  20 20 20 20 20 20 20 20-20 20 00 00 00 00 00 00             ......
1000:0030  01 00 02 00 03 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0040  04 00 05 00 06 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0050  05 00 07 00 09 00 00 00-00 00 00 00 00 00 00 00   ................
1000:0060  58 A3 76 00 58 A3 78 00-58 01 06 78 00 A1 76 00   X.v.X.x.X..x..v.
1000:0070  50 A1 78 00 C3 00 0A 00-09 00 00 00 00 00 00 00   P.x.............

• Save a byte by using the stock calling convention; return value already in AX. Pushing a return value onto the stack is really ugly. – Joshua Jan 27 at 20:33

## GNU-C-Preprocessor, 90 + O(48*n) Bytes

There should be code golf challenges for C preprocessor only, my idea:

#define T(x,...) x
#define D(x,y...) y
#define A(f,x,y) f(x,y)
#define Z5(f,x,y) A(f,T x,T y),Z4(f,(D x),(D y))
#define Z4(f,x,y) A(f,T x,T y),Z3(f,(D x),(D y))
#define Z3(f,x,y) A(f,T x,T y),Z2(f,(D x),(D y))
#define Z2(f,x,y) A(f,T x,T y),Z1(f,(D x),(D y))
#define Z1(f,x,y) A(f,T x,T y)


This example works for input lists of length <= 5. You can call the function like this:

#define C(x,y) x##y
(Z5(C,(h,e,l,l,o),(w,o,r,l,d)))


which results in

(hw,eo,lr,ll,od)


The "n" is the maximum list length to process and output. Of course you could use single characters instead of "Z" as name but using a digit allows for more flexibility and easier maintenance. For example you could add a parameter for "Z" to specify the bounds which is concatenated as Z##n and used as nested macro call.

The C Preprocessor opens a new realm of languages for code golf and is a rather minimal pure-functional primitive-recursive programming language which only features macro substitutions (which look like function calls, '(', ')', ',' and '#' are the only characters with special meaning), stringification, character concatenation (must be result in valid C tokens) and no direct recursion (each call must be defined with different macro name). Primitive-recursive means, it uses inputs of strictly bounded or fixed lengths and the program always terminates for all inputs. If you want a program for unbounded length, you would need an infinite program but program inputs are always bounded in practice. Literally any strictly limited output for strictly limited input values can be programmed by a primitive-recursive program and thus the C preprocessor. The program only must be big enough to compute it. It can become quite memory-inefficient though.

## GNU-C Preprocessor, 238 + O(32*log3(n)) Bytes

A more optimal program size with O(log(n)) program growth is possible but requires hacky additional macro definitions and will only pay off starting with slightly larger input lists. This example purposefully does not add commas between elements in the output to allow for higher flexibility in output.

#define L4(p) L3(p) L3(p) L3(p)
#define L3(p) L2(p) L2(p) L2(p)
#define L2(p) L1(p) L1(p) L1(p)
#define L1(p) p() p() p()

#define T(x,...) x
#define D(x,y...) y
#define I(x...) x
#define W(r,f,a,b,x,y) (I r f(a,b)),f,x,y
#define X(r,f,x,y) W(r,f,T x,T y,(D x),(D y))
#define Y(x...) X(x)
#define C() )
#define O() (
#define U(x...) T(x)
#define Z(f,x,y) U(U(L4(Y O)(),f,x,y L4(C)))


Explanation:

• L<x> is the loop and <x> is the exponent of how often to loop.
• T,U = take first element from comma-separated list
• D = drop first element from comma-separated list
• I = identity function (evaluates arguments and can remove parens)
• A = applies a function but without lazy argument-evaluation
• X,Y,Z = the actual zipWith function
• C = closing parenthesis, needed for code generation
• O = opening parenthesis, needed for code generation (prevents unclosed macro call errors)

The main idea is to code-generate a long nested expression in a loop so that Z(args) evaluates to literally Y ( Y ( Y ( ... args' ...) ) ) and Y reduces the arguments by one step when evaluated in U(U(...)) (it does not work with only one U).

This example can process up to 3^4 = 243 elements of two lists. If you want to multiply the processing limit by 3, add a new loop line on the top of the code. You need about 20 loop lines to handle unsigned 32-bit of elements. But keep in mind that each element is evaluated, even if does not exist! This is an example which zip-concatenates elements of two lists and places a comma between elements iff the concatenation is not empty:

#define V2(x...) ,##x
#define V(x...) V2(x)
#define CONC(x,y...) y##x
#define CONC1(x,y) V(CONC(y,x))
#define D2(x) (D x)
D2(Z(CONC1,(h,e,l,l,o),(w,o,r,l,d)))


The result is (hw ,eo ,lr ,ll ,od) . It works with any lists with any number of elements but will output at most 243 elements. Tested on godbolt.org.

If you find improvements which aren't only shorter macro names, please feel free to update this answer and leave a comment.

• Welcome to the site, and nice first answer! – caird coinheringaahing Jan 27 at 21:45
• And a nice tutorial on a language that is ignored and underappreciated. My only attempt at doing anything with the C preprocessor was implement a max() function, 30 years ago. – Mark Stewart Jan 29 at 0:39

# Husk, 5 bytes

mF₁Te


Try it online!

Takes the two arrays as arguments, and the function in the footer. You can use any binary function which operates on two TNums from Husk's Commands page.

## Explanation

mF₁Te Main program: accepts two arrays
e two element list from the arrays
T  transpose
m     map each pair to
F    itself folded by
₁   the black box function

• Yeah, that's why I submitted a non-trivial solution. – Razetime Jan 26 at 12:15
• Ah ok, thought you might have overlooked the builtin answer accidently. Carry on. :) – Kevin Cruijssen Jan 26 at 12:17
• What if the black box function isn't representable as a single builtin? – Joshua Jan 26 at 20:11
• @Joshua - This still works fine. For instance, test case 3... – Dominic van Essen Jan 26 at 23:43

# Rust, 34 bytes

|f,a,b|a.into_iter().zip(b).map(f)


Try it online!

Rust doesn't have a zipwith function but it does have a zip and a map function. Returns an iterator over the values.

# R, 32 bytes

function(f,x,y)Vectorize(f)(x,y)


Try it online!

If R didn't already have a built-in (mapply/Map) for zipwidth, here's how one could implement it in 32 bytes...
(...and if R didn't have Vectorize, we'd need to implement it the 'hard' way for 54 bytes: function(f,x,y){for(i in seq(a=x))T[i]=f(x[i],y[i]);T})

• I would argue this makes Vectorize an equivalent built-in solution to this problem, except that its first input is curried – pxeger Jan 26 at 11:38
• @pxeger - I kind-of agree, ish, so I've now included a 'do it the hard way' version, too... – Dominic van Essen Jan 26 at 11:40
• I think Map would also work for R as a built-in, since it's really just Map=function(f,...)mapply(FUN=f...,simplify=FALSE) – Giuseppe Jan 26 at 17:02
• @Giuseppe - you're absolutely right! I always seem to forget about Map. I've edited it into the 'trivial builtin answers' answer. Thanks for the reminder... – Dominic van Essen Jan 26 at 17:40

# Factor, 24 bytes

[ zip swap f assoc>map ]


Try it online!

Avoids the obvious built-in 2map and its trivial generalizations nmap and 2map-as, and uses zip and assoc>map instead (so it works like e.g. the Rust answer). Takes the input from the stack in the order of func arr1 arr2.

Small golfing tip: when using a function that takes an "exemplar" (which specifies the type of the output container), supplying f has the same effect as supplying { } (a plain array).

## Factor, using nmap, 10 bytes

[ 2 nmap ]


Try it online!

nmap is a generalized version of map-family of functions that works with n input arrays and n-ary functions. Obviously, 2 nmap works on two input arrays and binary functions.

## Factor, using 2map-as, 13 bytes

[ f 2map-as ]


Try it online!

2map-as is a simple variation of 2map that takes an exemplar.

2map itself is already listed in the trivial built-in catalogue.

• f means { } as an exemplar? Whaaaaaaat. That's awesome. – chunes Apr 28 at 2:17

# JavaScript (ES6), 32 bytes

(F,a,b)=>a.map((x,i)=>F(x,b[i]))


Try it online!

# Zsh, 24 bytes

for 1 2 (paste $@)f$@


Try it online!

Uses a black-box function named f, with input from two files specified in command line arguments.

However, paste alone is really just a zip, and this brings up some interesting debate depending on your interpretation of what counts as a "function" under the Unix philosophy of "everything should be a text stream":

#!/bin/zsh

# using a predefined function F
z1() { paste $1$2 | F }
# using a function named in the arguments
z2() { paste $1$2 | $3 } F() { while read line; do rev <<< line; done } z1 input1 input z2 input1 input2 F paste input1 input2 | F paste input1 input2 | rev # so maybe paste is just a point-free function? paste  So maybe this is a trivial built-in solution: Unix shell (?), 5 bytes (?) paste  # Java 8, 52 bytes A->B->{for(int l=A.length;l-->0;)A[l]=f(A[l],B[l]);}  Puts the result in the first argument instead of returning a new integer-array (to save 25 bytes). Try it online. Explanation: A->B->{ // Method with two integer-array parameters & no return-type for(int l=A.length;l-->0;)// Loop l in the range (length,0]: A[l]= // Change the l'th item in the first array to: f( // Call the black-box function f A[l],B[l]);} // with the l'th items of both arrays as parameters  Black box input format: Assumes a named function Object f(Object a,Object b) is present, which is allowed according to this meta answer. I have an abstract class Test containing the default function f(a,b), as well as the lambda above: abstract class Test{ Object f(Object a,Object b){ return(int)a+(int)b; } public java.util.function.Function<Object[], java.util.function.Consumer<Object[]>>c= A->B->{for(int l=A.length;l-->0;)A[l]=f(A[l],B[l]);} ; }  For the test cases, I overwrite this function f. For example, the third test case (which is responsible for the black-box having Object as parameters/return-type instead of int) is called like this: Object[] a = new Object[]{1,2,3}; new Test(){ @Override Object f(Object a,Object b){ return a==b; } }.c.apply(a).accept(new Object[]{3,2,1}); System.out.println(java.util.Arrays.toString(a));  • Using a predefined function is no longer valid in Java, as since Java 8 it is possible to pass the function as a parameter. The meta answer in your link does not apply here. – Donat Jan 31 at 14:48 • It'll just cost a couple bytes to use (f,A,B)->, right? – user Jan 31 at 17:48 • @user Not really.. It would be 68 bytes in that case, since you also need two .apply() to use lambda f. :/ Which is why I think meta rules of the form "If X isn't possible, use Y instead" are unfair for any language where both are possible, but Y would be shorter.. If we're allowed to pass an interface as argument instead of Function it could be 57 bytes. Although an interface is technically not a function, so no idea if it's allowed.. (Using Java 7 without lambdas at all would be 73 bytes.) – Kevin Cruijssen Jan 31 at 18:34 # Nim, 71 70 bytes iterator z[I](f:proc(x,y:I):I;a,b:seq):I= for i,x in a:yield x.f b[i]  Try it online! # Idris - 77 bytes z:(a->b->c)->List a->List b->List c z f(x::v)(y::w)=f x y::z f v w z _ _ _=[]  • Welcome to the site, and nice first answer! – caird coinheringaahing Jan 27 at 16:32 • I don't know Idris, but if the pattern matching works like in Haskell, you should be able to remove the first two patterns and append z _ _ _=[] at the end. – ovs Jan 27 at 16:40 • Welcome to Code Golf! I would suggest linking to Try it Online or other online interpreters along with your answer. Here is a 77 byte version of your answer. As @ovs said, you can collapse the first two patterns into one because the lists are guaranteed to be of the same length. – user Jan 27 at 16:47 • @ovs user Ah I appreciate the advice, I learnt a new technique for programming in Idris and better codegolf etiquette because of both of you! I've updated the answer accordingly. – nicoty Jan 27 at 19:01 # GNU sed, 55 + 1(r flag) = 56 bytes GNU sed doesn't even have integers, not to mention functions, lists / arrays, etc. In order to participate, I used the following assumptions for the I/O methods, which hopefully respect the consensus regarding sed: 1. each input list / array is given on a separate input line, with the integers separated by space. 2. the black-box function is predefined under the label f. 3. the black-box function arguments are two space separated integers read from the first pattern space line (generated there at runtime during each iteration). 4. the return value of the black-box function is placed on the first pattern space line 5. the output list / array is given as one integer per line. 1N # read the 2nd input list s:([^ ]+) ?(.*\n)([^ ]+) ?(.*):\1 \3\n\2\4: # put list1[i] and list2[i] on first line tf;: # call the black-box function f P # print the function return value D # delete return value, start new iteration  Try it online with sed 4.2.2! The implemented black-box function in the link above is a == b. The other test functions are significantly harder to implement in sed, but possible since the language is Turing complete. # Wolfram Language (Mathematica), 11 bytes Inner[##,]&  Try it online! Inner almost does zipwith - except, by default, it returns the sum of the results. ### 12 bytes #@@@(#2)&  Try it online!  is \[Transpose]. ## Stax, 6 bytes invocable, or 3 bytes toplevel Does not make sense to compress functions in stax so I didn't. The two arrays are passed on the stack, and the black-box function is passed in the x register. |\{x!m  Explained: |\ Zip input arrays { x push contents of x register ! invoke top of stack m map  Program head version: two arrays are passed to it on the stack; body of blackbox program follows |\m  Explained: |\ Zip input arrays m map array with rest of program  # Red, 69 bytes func[a b f][collect[repeat i length? a[keep compose[(f a/:i b/:i)]]]]  Try it online! ## Printing the result instead of returning it: ## 61 bytes func[a b f][repeat i length? a[print compose[(f a/:i b/:i)]]]  Try it online! # Racket, 41 bytes (λ(x y f)(for/list([i x][j y])(f i j))))  Try it online! for/list is almost the necessary builtin, just needs some additional syntax. • zipwith – D. Ben Knoble Jan 26 at 22:56 • @D.BenKnoble That would go into the Trivial Answers CW answer, right? – user Jan 26 at 23:54 • @user yes, left a comment there too. – D. Ben Knoble Jan 27 at 1:03 • @D.BenKnoble I know just a little Racket. I'm sure I searched for zipwith (and just zip) in the local documentation and I didn't find anything. Now I see that zipwith is not part ot the core Racket, but of a langauge called "Heresy". – Galen Ivanov Jan 27 at 7:45 • @GalenIvanov yeah I didnt even notice it was until I looked harder at the link I posted. Sorry – D. Ben Knoble Jan 27 at 12:46 # Scala 3 (compile time), 87 bytes type Z[F[_,_],A<:Tuple,B]<:Tuple=(A,B)match{case(a*:b,c*:d)=>F[a,c]*:Z[F,b,d]case _=>A}  Try it online! Takes tuples instead of lists. This isn't particulary interesting or complicated, but here's an explanation: type Z[ //Declare a type Z taking these arguments F[_,_], //A type F that itself takes two arguments A <: Tuple, //The first list, a tuple B //The second list (actually also a tuple) ] <: Tuple = //The returned type is also a tuple (A, B) match { //Make a tuple (A, B) and match it: //h*:t represents a tuple with head h and tail t (like :: or :) case (a *: b, c *: d) => //If A is of the form a*:b and B is of the form c*:d F[a, c] //Call F on the heads *: //And prepend that to Z[F, b, d] //The result of zipping the rest of the tuples case _ => A //Otherwise, both tuples are empty, so we return an empty tuple }  # Pure Zsh, 31 bytes b=($=3)
for 2 3 (${${=2}:^b})$@  Try it online! Takes parameters function name, space-delimited list, space-delimited list With -o shwordsplit, one byte is saved with b=($3) instead.

b=($=3) # =split$3 and save as array $b #${${=2}:^b} # Zip$=2 and $b together for 2 3 (${${=2}:^b}) # Loop over 2 at a time as$2 and $3 (argv[2], argv[3])$@                   # $1$2 \$3


# Groovy, 36 bytes

{f,a,b->[a,b].transpose().collect f}


Try it online!

# Python 3, 37 35 bytes

lambda f,*a:[f(*t)for t in zip(*a)]


Try it online!

• You can save two bytes by using more *: lambda f,*a:[f(*t)for t in zip(*a)] – ovs Jan 31 at 12:54
• @ovs: how cool is that ?!?! Thanks. – mhawke Jan 31 at 13:00

# Python 3, 19 bytes

lambda*l:[*map(*l)]


Try it online!

• Very nice answer! – Donat Feb 1 at 14:10

# Vyxal, 5 bytes

£Zƛ¥R


Try it Online!

I don't know how to input functions properly in Vyxal, so the Vyxal interpreter link just has the function stuffed with the code.

£Zƛ¥R
£     Set the register to the blackbox function
Z    Zip the two lists
ƛ   For each element of that
R Reduce using
¥  The function in the register


# Icon, 52 bytes

procedure z(a,b,f)
write(f(a[i:=1to*a],b[i]))&\y
end


Try it online!

# CJam, 8 bytes

l'.l++~p


Try it online! Or verify the first three cases: 1, 2, 3.

Input is a line with (the string representation of) two arrays, and then a line with (the string representation of) the function, defined as a CJam block.

### How it works

l    e# Read line containing the two arrays
'.   e# Push character "." (which will do the actual job)
l    e# Read line containing a code block
++   e# Concatenate twice
~    e# Evaluate as CJam code
p    e# Print

• Well if you can print your output rather than produce an output array, so can I. – Joshua Jan 26 at 20:09

# Lua, 63 bytes

load'o,f,a,b={},...for i=1,#a do o[i]=f(a[i],b[i])end return o'


Try it online!

Functions are 1st-class values in Lua, so this is pretty simple (if a bit long because of it's need to iterate).

## Batch, 99 bytes

@set/pf=
@set/ps=
@call:c %s%
@exit/b
:c
@set/ps=
@for %%i in (%s%)do @call %f% %%1 %%i&shift


Takes three lines of input, the command or batch function to execute, followed by two space or comma separated lists. Works fine with numbers but anything needing quoting is likely to crash and burn. Explanation:

@set/pf=


Input the command or function to execute.

@set/ps=


Input the first list.

@call:c %s%


Call the subroutine, expanding the list into separate parameters.

@exit/b


Exit the program.

:c


Declare the subroutine.

@set/ps=


Input the second list.

@for %%i in (%s%)do


Expand the list, looping over each element.

@call %f% %%1 %%i&shift


Call the command or function, passing in the current element from both the first and current list, then shift the element from the first list. The use of call here both allows the function to be a batch script but also allows the expansion of %1 to be delayed using an extra % so that it uses the most recently shifted value.