g | x | w | all
Bytes Lang Time Link
066Tcl250515T211331Zsergiol
018Juby250515T191115ZJordan
nan210126T105737Zcaird co
003Japt230110T081059ZShaggy
037Arturo230110T051727Zchunes
nan221005T134303Zbigyihsu
059QBasic221016T041031ZDLosc
032R210126T113116ZDominic
026Ruby221014T181035ZJordan
00605AB1E221005T085538ZKevin Cr
005Pip xp221005T050152ZDLosc
011Wolfram Language Mathematica210126T181522Zatt
005Vyxal210531T183058Zuser
nanPerl 5210126T145812ZNahuel F
019Python 3210201T061706ZDanis
093PowerShell210201T053832Zwasif
034Ruby210131T142651ZDonat
035Python 3210128T233128Zmhawke
037Io210131T125003ZDonat
036Groovy210131T120533ZDonat
014x8616 machine code210127T100730Zuser9915
9048GNUCPreprocessor210127T213135ZChrisoLo
nanGNU sed210127T194545Zseshouma
077Idris210127T162227Znicoty
099Batch210127T120208ZNeil
049C gcc210126T121233ZEasyasPi
063Lua210127T045254ZBenrob03
022Haskell210127T000040Zorthocre
024Factor210126T233226ZBubbler
002Husk210126T225523ZLeo
006Stax210126T195605ZJoshua
031Pure Zsh210126T190842ZGammaFun
087Scala 3 compile time210126T174700Zuser
041Racket210126T151030ZGalen Iv
034Rust210126T150900ZAiden4
070Nim210126T142642ZAdam
069Red210126T122538ZGalen Iv
008CJam210126T125100ZLuis Men
052Icon210126T124714ZGalen Iv
052Java 8210126T123522ZKevin Cr
005Husk210126T120816ZRazetime
024Zsh210126T120612Zpxeger
032JavaScript ES6210126T110413ZArnauld

Tcl, 66 bytes

proc Z {f L M} {lmap x $L y $M {expr [string map {a $x b $y} $f]}}

Try it online!

Tcl parser substitution is delicious!

J-uby, 18 bytes

Takes curried arguments like F[g][a,b] where g is a function and a and b are the arrays.

:+@|:& &:*|:|&:zip

Attempt This Online!

This exposes a bug in J-uby with the + operator (:+@ above). +F is a shortcut for :<< & F, i.e. binding F to the << operator. The << operator is used like F << a, where it "splats" the array a as individual arguments to F, i.e. F.(*a). However, this doesn't work for all functions. In the ATO above you can see it works fine for the function :+, which adds its two arguments. However, it doesn't work for ->x,y{ x+y }, which also takes two arguments. I'd love to know why.

Trivial builtin answers

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


HBL, 0.5 bytes

2

(Not implemented in the online version at the time of this writing.)

Fig, \$1\log_{256}(96)\approx\$ 0.823 bytes

z

Try it online!

Same as Husk.

APL (Dyalog Unicode), 1 byte

¨

Try it online!

BQN, 1 byte

¨

Same as APL. Try it at BQN online!

Husk, 1 byte

z

Try it online!

Jelly, 1 byte

"

Try it online!

J, 2 bytes

"0

Try it online!

Not technically a builtin, but close enough.

C#, 3 bytes

Zip

Try it online!

Google Sheets/Excel, 3 bytes

MAP

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!

tinylisp, 18 bytes

Not exactly a builtin, but a library function:

map*

4 bytes for the function, 14 bytes for (load library). Try it online!

Japt, 3 bytes

The same byte count as using the interleave built-in.

Takes the arrays as input as a single, 2D array. Black box function is pre-assigned to variable V.

yrV

Try it

Arturo, 37 bytes

$[f a b][map combine a b'x->f x\0x\1]

Try it

Go, 90 89 bytes

func z[T any](f func(T,T)T,l,r[]T)(o[]T){for i:=range l{o=append(o,f(l[i],r[i]))}
return}

Attempt This Online!

In a strange reversal, this answer is 1 byte shorter if made generic. Usually the generic version is more verbose.

Old answer, ints only, 90 bytes

func(f func(int,int)int,l,r[]int)(o[]int){for i:=range l{o=append(o,f(l[i],r[i]))}
return}

Attempt This Online!

The fun thing about Go is that you have to implement things like the functional programming functions "from scratch" every single time.

QBasic, 59 bytes

SUB z(a(),b(),n)
FOR i=1TO n
a(i)=f(a(i),b(i))
NEXT
END SUB

Defines a subprogram z that takes two arrays and their length, calls a predefined function f on pairs of elements, and stores the results back into the first array. It can be invoked either as CALL z(a(), b(), n) or as z a(), b(), n.

Explanation

The tricky part was figuring out a way to do this at all in QBasic, since it doesn't have first-class functions, function pointers, or the ability to return arrays from functions. Once I found a combination of technically allowed approaches that would work, the code itself was pretty straightforward: loop over each index i and set a(i) to f(a(i), b(i)).

Testing instructions

You can run QBasic code at Archive.org. Here's a full program that runs the third test case:

CLS
CONST LENGTH = 4
DIM a(LENGTH), b(LENGTH)

DATA 6, 8, 1, 3
DATA 5, 3, 6, 2

FOR i = 1 TO LENGTH
  READ a(i)
NEXT i
FOR i = 1 TO LENGTH
  READ b(i)
NEXT i

CALL z(a(), b(), LENGTH)

FOR i = 1 TO LENGTH
  PRINT a(i);
NEXT i
PRINT

FUNCTION f(x, y)
f = x ^ 2 + y \ 2
END FUNCTION

SUB z(a(),b(),n)
FOR i=1TO n
a(i)=f(a(i),b(i))
NEXT
END SUB

Note: as soon as you type the FUNCTION or SUB line, the editor will automatically add the corresponding END FUNCTION or END SUB and put the new function/subprogram in its own window. To add the sub after entering the function, position your cursor below the END FUNCTION line and start typing from SUB z(.... Once you've entered both the function and the sub, you can run the program using Run > Start (or press F5). You can also switch between the sub, function, and main program using View > SUBs (or press F2).

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 zipwith, 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})

Ruby, 26 bytes

Like Donat’s answer, just tighter syntax and doesn’t have to monkey-patch Array.

->(f,a,b){a.zip(b).map &f}

Attempt This Online!

05AB1E, 6 bytes

øε`I.V

Try it online or verify all test cases.

05AB1E lacks functions, but strings can be used with an eval builtin to mimic the behavior of functions (which I've also used pretty often for recursive [ragged-list] challenges).

Explanation:

ø       # Zip the first two (implicit) input-lists
 ε      # Map over each pair:
  `     #  Pop and push the contents of the pair separated to the stack
   I    #  Push the third input function-string
    .V  #  Evaluate and execute it as 05AB1E code
        # (after which the resulting list is output implicitly)

Pip -xp, 5 bytes

aMZbc

Try It Online!

Explanation

This is essentially a built-in, the ternary MZ (map-zip) operator. However, I'm pretty sure a bare operator isn't a valid solution, so I've made it a full program. The -x flag allows functions and lists to be evaluated as such when passed as command-line arguments, and the -p flag means lists will be output in list format rather than concatenated together.

Wolfram Language (Mathematica), 11 bytes

Inner[##,]&

Try it online!

Returns a Null of results.

Inner almost does zipwith - except, by default, it returns the sum of the results.


Also 11 bytes

#@@@In@@#2&

Try it online!

In (among others) is as short as a built-in Listable function gets, and remains unevaluated with >1 argument.

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

Perl 5, 46 bytes, 37 bytes

-9 bytes thanks to Kjetil S.

sub{map$_[0](@_[$_,$_+@_/2]),1..@_/2}

Try it online!

Python 3, 19 bytes

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

Try it online!

PowerShell, 93 bytes

function Z($F,$S,$R={,$args}){[Linq.Enumerable]::Zip($F,$S,[Func[Object,Object,Object[]]]$R)}

Example

Z (1,2,3) (4,5,6) {param($a, $b) ($a+$b)}
'-'
Z (8,3) (3,8) {param($a, $b) ($a-$b)}
'-'
Z (6,8,1,3) (5,3,6,2) {param($a, $b) ([math]::pow($a,2)+$b/2)}
'-'
Z (1,2,3) (3,2,1) {param($a, $b) ([int]($a-eq$b))}

Try it online!

It is a zip function which can take map expression as its third parameter to know how to pass it then see the demo in the Try it online "Footer" section how to use it.

Ruby, 34 bytes

def z(f,b)zip(b).map{|p|f.(*p)}end

Try it online!

Python 3, 37 35 bytes

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

Try it online!

Io, 37 bytes

method(f,a,b,a map(i,x,f(x,b at(i))))

Try it online!

Groovy, 36 bytes

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

Try it online!

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:

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
C3            RET             ; return to caller

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
1000:0000 AD            LODSW
-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.............

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:

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.

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.

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 _ _ _=[]

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.

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.

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).

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))

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.

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!

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

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

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
  }

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.

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.

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!

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!

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

Icon, 52 bytes

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

Try it online!

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));

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

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

JavaScript (ES6), 32 bytes

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

Try it online!