@@ -1119,7 +1119,27 @@ defmodule Stream do
1119
1119
"""
1120
1120
@ spec zip ( Enumerable . t ( ) , Enumerable . t ( ) ) :: Enumerable . t ( )
1121
1121
def zip ( enumerable1 , enumerable2 ) do
1122
- zip ( [ enumerable1 , enumerable2 ] )
1122
+ zip_with ( enumerable1 , enumerable2 , fn left , right -> { left , right } end )
1123
+ end
1124
+
1125
+ @ doc """
1126
+ Zips corresponding elements from a finite collection of enumerables
1127
+ into one stream of tuples.
1128
+
1129
+ The zipping finishes as soon as any enumerable in the given collection completes.
1130
+
1131
+ ## Examples
1132
+
1133
+ iex> concat = Stream.concat(1..3, 4..6)
1134
+ iex> cycle = Stream.cycle(["foo", "bar", "baz"])
1135
+ iex> Stream.zip([concat, [:a, :b, :c], cycle]) |> Enum.to_list()
1136
+ [{1, :a, "foo"}, {2, :b, "bar"}, {3, :c, "baz"}]
1137
+
1138
+ """
1139
+ @ doc since: "1.4.0"
1140
+ @ spec zip ( enumerables ) :: Enumerable . t ( ) when enumerables: [ Enumerable . t ( ) ] | Enumerable . t ( )
1141
+ def zip ( enumerables ) do
1142
+ zip_with ( enumerables , & List . to_tuple ( & 1 ) )
1123
1143
end
1124
1144
1125
1145
@ doc """
@@ -1139,28 +1159,28 @@ defmodule Stream do
1139
1159
"""
1140
1160
@ doc since: "1.12.0"
1141
1161
@ spec zip_with ( Enumerable . t ( ) , Enumerable . t ( ) , ( term , term -> term ) ) :: Enumerable . t ( )
1142
- def zip_with ( enumerable1 , enumerable2 , zip_fun ) when is_function ( zip_fun , 2 ) do
1143
- zip_with ( [ enumerable1 , enumerable2 ] , & apply ( zip_fun , & 1 ) )
1162
+ def zip_with ( enumerable1 , enumerable2 , zip_fun )
1163
+ when is_list ( enumerable1 ) and is_list ( enumerable2 ) and is_function ( zip_fun , 2 ) do
1164
+ & zip_pair ( enumerable1 , enumerable2 , & 1 , & 2 , zip_fun )
1144
1165
end
1145
1166
1146
- @ doc """
1147
- Zips corresponding elements from a finite collection of enumerables
1148
- into one stream of tuples.
1167
+ def zip_with ( enumerable1 , enumerable2 , zip_fun ) when is_function ( zip_fun , 2 ) do
1168
+ zip_with ( [ enumerable1 , enumerable2 ] , fn [ left , right ] -> zip_fun . ( left , right ) end )
1169
+ end
1149
1170
1150
- The zipping finishes as soon as any enumerable in the given collection completes.
1171
+ defp zip_pair ( _list1 , _list2 , { :halt , acc } , _fun , _zip_fun ) do
1172
+ { :halted , acc }
1173
+ end
1151
1174
1152
- ## Examples
1175
+ defp zip_pair ( list1 , list2 , { :suspend , acc } , fun , zip_fun ) do
1176
+ { :suspended , acc , & zip_pair ( list1 , list2 , & 1 , fun , zip_fun ) }
1177
+ end
1153
1178
1154
- iex> concat = Stream.concat(1..3, 4..6)
1155
- iex> cycle = Stream.cycle(["foo", "bar", "baz"])
1156
- iex> Stream.zip([concat, [:a, :b, :c], cycle]) |> Enum.to_list()
1157
- [{1, :a, "foo"}, {2, :b, "bar"}, {3, :c, "baz"}]
1179
+ defp zip_pair ( [ ] , _list2 , { :cont , acc } , _fun , _zip_fun ) , do: { :done , acc }
1180
+ defp zip_pair ( _list1 , [ ] , { :cont , acc } , _fun , _zip_fun ) , do: { :done , acc }
1158
1181
1159
- """
1160
- @ doc since: "1.4.0"
1161
- @ spec zip ( enumerables ) :: Enumerable . t ( ) when enumerables: [ Enumerable . t ( ) ] | Enumerable . t ( )
1162
- def zip ( enumerables ) do
1163
- zip_with ( enumerables , & List . to_tuple ( & 1 ) )
1182
+ defp zip_pair ( [ head1 | tail1 ] , [ head2 | tail2 ] , { :cont , acc } , fun , zip_fun ) do
1183
+ zip_pair ( tail1 , tail2 , fun . ( zip_fun . ( head1 , head2 ) , acc ) , fun , zip_fun )
1164
1184
end
1165
1185
1166
1186
@ doc """
@@ -1188,10 +1208,41 @@ defmodule Stream do
1188
1208
@ spec zip_with ( enumerables , ( Enumerable . t ( ) -> term ) ) :: Enumerable . t ( )
1189
1209
when enumerables: [ Enumerable . t ( ) ] | Enumerable . t ( )
1190
1210
def zip_with ( enumerables , zip_fun ) when is_function ( zip_fun , 1 ) do
1191
- & prepare_zip ( enumerables , & 1 , & 2 , zip_fun )
1211
+ if is_list ( enumerables ) and :lists . all ( & is_list / 1 , enumerables ) do
1212
+ & zip_list ( enumerables , & 1 , & 2 , zip_fun )
1213
+ else
1214
+ & zip_enum ( enumerables , & 1 , & 2 , zip_fun )
1215
+ end
1216
+ end
1217
+
1218
+ defp zip_list ( _enumerables , { :halt , acc } , _fun , _zip_fun ) do
1219
+ { :halted , acc }
1220
+ end
1221
+
1222
+ defp zip_list ( enumerables , { :suspend , acc } , fun , zip_fun ) do
1223
+ { :suspended , acc , & zip_list ( enumerables , & 1 , fun , zip_fun ) }
1224
+ end
1225
+
1226
+ defp zip_list ( enumerables , { :cont , acc } , fun , zip_fun ) do
1227
+ case zip_list_heads_tails ( enumerables , [ ] , [ ] ) do
1228
+ { heads , tails } -> zip_list ( tails , fun . ( zip_fun . ( heads ) , acc ) , fun , zip_fun )
1229
+ :error -> { :done , acc }
1230
+ end
1231
+ end
1232
+
1233
+ defp zip_list_heads_tails ( [ [ head | tail ] | rest ] , heads , tails ) do
1234
+ zip_list_heads_tails ( rest , [ head | heads ] , [ tail | tails ] )
1235
+ end
1236
+
1237
+ defp zip_list_heads_tails ( [ [ ] | _rest ] , _heads , _tails ) do
1238
+ :error
1239
+ end
1240
+
1241
+ defp zip_list_heads_tails ( [ ] , heads , tails ) do
1242
+ { :lists . reverse ( heads ) , :lists . reverse ( tails ) }
1192
1243
end
1193
1244
1194
- defp prepare_zip ( enumerables , acc , fun , zip_fun ) do
1245
+ defp zip_enum ( enumerables , acc , fun , zip_fun ) do
1195
1246
step = fn x , acc ->
1196
1247
{ :suspend , :lists . reverse ( [ x | acc ] ) }
1197
1248
end
@@ -1201,42 +1252,42 @@ defmodule Stream do
1201
1252
{ & Enumerable . reduce ( enum , & 1 , step ) , [ ] , :cont }
1202
1253
end )
1203
1254
1204
- do_zip ( enum_funs , acc , fun , zip_fun )
1255
+ do_zip_enum ( enum_funs , acc , fun , zip_fun )
1205
1256
end
1206
1257
1207
- # This implementation of do_zip /4 works for any number of streams to zip
1208
- defp do_zip ( zips , { :halt , acc } , _fun , _zip_fun ) do
1258
+ # This implementation of do_zip_enum /4 works for any number of streams to zip
1259
+ defp do_zip_enum ( zips , { :halt , acc } , _fun , _zip_fun ) do
1209
1260
do_zip_close ( zips )
1210
1261
{ :halted , acc }
1211
1262
end
1212
1263
1213
- defp do_zip ( zips , { :suspend , acc } , fun , zip_fun ) do
1214
- { :suspended , acc , & do_zip ( zips , & 1 , fun , zip_fun ) }
1264
+ defp do_zip_enum ( zips , { :suspend , acc } , fun , zip_fun ) do
1265
+ { :suspended , acc , & do_zip_enum ( zips , & 1 , fun , zip_fun ) }
1215
1266
end
1216
1267
1217
- defp do_zip ( [ ] , { :cont , acc } , _callback , _zip_fun ) do
1268
+ defp do_zip_enum ( [ ] , { :cont , acc } , _callback , _zip_fun ) do
1218
1269
{ :done , acc }
1219
1270
end
1220
1271
1221
- defp do_zip ( zips , { :cont , acc } , callback , zip_fun ) do
1272
+ defp do_zip_enum ( zips , { :cont , acc } , callback , zip_fun ) do
1222
1273
try do
1223
- do_zip_next_tuple ( zips , acc , callback , [ ] , [ ] , zip_fun )
1274
+ do_zip_next ( zips , acc , callback , [ ] , [ ] , zip_fun )
1224
1275
catch
1225
1276
kind , reason ->
1226
1277
do_zip_close ( zips )
1227
1278
:erlang . raise ( kind , reason , __STACKTRACE__ )
1228
1279
else
1229
1280
{ :next , buffer , acc } ->
1230
- do_zip ( buffer , acc , callback , zip_fun )
1281
+ do_zip_enum ( buffer , acc , callback , zip_fun )
1231
1282
1232
1283
{ :done , _acc } = other ->
1233
1284
other
1234
1285
end
1235
1286
end
1236
1287
1237
- # do_zip_next_tuple /6 computes the next tuple formed by
1288
+ # do_zip_next /6 computes the next tuple formed by
1238
1289
# the next element of each zipped stream.
1239
- defp do_zip_next_tuple (
1290
+ defp do_zip_next (
1240
1291
[ { _ , [ ] , :halt } | zips ] ,
1241
1292
acc ,
1242
1293
_callback ,
@@ -1248,15 +1299,15 @@ defmodule Stream do
1248
1299
{ :done , acc }
1249
1300
end
1250
1301
1251
- defp do_zip_next_tuple ( [ { fun , [ ] , :cont } | zips ] , acc , callback , yielded_elems , buffer , zip_fun ) do
1302
+ defp do_zip_next ( [ { fun , [ ] , :cont } | zips ] , acc , callback , yielded_elems , buffer , zip_fun ) do
1252
1303
case fun . ( { :cont , [ ] } ) do
1253
1304
{ :suspended , [ elem | next_acc ] , fun } ->
1254
1305
next_buffer = [ { fun , next_acc , :cont } | buffer ]
1255
- do_zip_next_tuple ( zips , acc , callback , [ elem | yielded_elems ] , next_buffer , zip_fun )
1306
+ do_zip_next ( zips , acc , callback , [ elem | yielded_elems ] , next_buffer , zip_fun )
1256
1307
1257
1308
{ _ , [ elem | next_acc ] } ->
1258
1309
next_buffer = [ { fun , next_acc , :halt } | buffer ]
1259
- do_zip_next_tuple ( zips , acc , callback , [ elem | yielded_elems ] , next_buffer , zip_fun )
1310
+ do_zip_next ( zips , acc , callback , [ elem | yielded_elems ] , next_buffer , zip_fun )
1260
1311
1261
1312
{ _ , [ ] } ->
1262
1313
# The current zipped stream terminated, so we close all the streams
@@ -1266,7 +1317,7 @@ defmodule Stream do
1266
1317
end
1267
1318
end
1268
1319
1269
- defp do_zip_next_tuple (
1320
+ defp do_zip_next (
1270
1321
[ { fun , zip_acc , zip_op } | zips ] ,
1271
1322
acc ,
1272
1323
callback ,
@@ -1276,10 +1327,10 @@ defmodule Stream do
1276
1327
) do
1277
1328
[ elem | rest ] = zip_acc
1278
1329
next_buffer = [ { fun , rest , zip_op } | buffer ]
1279
- do_zip_next_tuple ( zips , acc , callback , [ elem | yielded_elems ] , next_buffer , zip_fun )
1330
+ do_zip_next ( zips , acc , callback , [ elem | yielded_elems ] , next_buffer , zip_fun )
1280
1331
end
1281
1332
1282
- defp do_zip_next_tuple ( [ ] = _zips , acc , callback , yielded_elems , buffer , zip_fun ) do
1333
+ defp do_zip_next ( [ ] = _zips , acc , callback , yielded_elems , buffer , zip_fun ) do
1283
1334
# "yielded_elems" is a reversed list of results for the current iteration of
1284
1335
# zipping. That is to say, the nth element from each of the enums being zipped.
1285
1336
# It needs to be reversed and passed to the zipping function so it can do it's thing.
0 commit comments