Stream merge: Difference between revisions

36,304 bytes added ,  2 months ago
Added FreeBASIC
(Added Sidef)
(Added FreeBASIC)
 
(35 intermediate revisions by 14 users not shown)
Line 14:
=={{header|360 Assembly}}==
No usage of tricks such as forbiden records in the streams.
<langsyntaxhighlight lang="360asm">* Stream Merge 07/02/2017
STRMERGE CSECT
USING STRMERGE,R13 base register
Line 130:
PG DS CL64
YREGS
END STRMERGE</langsyntaxhighlight>
{{in}}
<pre style="height:20ex">
Line 165:
Line 055
</pre>
 
=={{header|Ada}}==
<syntaxhighlight lang="ada">with Ada.Text_Io;
with Ada.Command_Line;
with Ada.Containers.Indefinite_Holders;
 
procedure Stream_Merge is
 
package String_Holders
is new Ada.Containers.Indefinite_Holders (String);
 
use Ada.Text_Io, String_Holders;
 
type Stream_Type is
record
File : File_Type;
Value : Holder;
end record;
 
subtype Index_Type is Positive range 1 .. Ada.Command_Line.Argument_Count;
Streams : array (Index_Type) of Stream_Type;
 
procedure Fetch (Stream : in out Stream_Type) is
begin
Stream.Value := (if End_Of_File (Stream.File)
then Empty_Holder
else To_Holder (Get_Line (Stream.File)));
end Fetch;
 
function Next_Stream return Index_Type is
Index : Index_Type := Index_Type'First;
Value : Holder;
begin
for I in Streams'Range loop
if Value.Is_Empty and not Streams (I).Value.Is_Empty then
Value := Streams (I).Value;
Index := I;
elsif not Streams (I).Value.Is_Empty and then Streams (I).Value.Element < Value.Element then
Value := Streams (I).Value;
Index := I;
end if;
end loop;
if Value.Is_Empty then
raise Program_Error;
end if;
return Index;
end Next_Stream;
 
function More_Data return Boolean
is (for some Stream of Streams => not Stream.Value.Is_Empty);
 
begin
 
if Ada.Command_Line.Argument_Count = 0 then
Put_Line ("Usage: prog <file1> <file2> ...");
Put_Line ("Merge the sorted files file1, file2...");
return;
end if;
 
for I in Streams'Range loop
Open (Streams (I).File, In_File, Ada.Command_Line.Argument (I));
Fetch (Streams (I));
end loop;
 
while More_Data loop
declare
Stream : Stream_Type renames Streams (Next_Stream);
begin
Put_Line (Stream.Value.Element);
Fetch (Stream);
end;
end loop;
 
end Stream_Merge;</syntaxhighlight>
 
=={{header|ALGOL 68}}==
NB, all the files (including the output files) must exist before running this. The output files are overwritten with the merged records.
<langsyntaxhighlight lang="algol68"># merge a number of input files to an output file #
PROC mergenf = ( []REF FILE inf, REF FILE out )VOID:
BEGIN
Line 270 ⟶ 344:
# test the file merge #
merge2( "in1.txt", "in2.txt", "out2.txt" );
mergen( ( "in1.txt", "in2.txt", "in3.txt", "in4.txt" ), "outn.txt" )</langsyntaxhighlight>
{{out}}
<pre>
Line 276 ⟶ 350:
 
=={{header|ATS}}==
<syntaxhighlight lang="ats">
<lang ATS>
(* ****** ****** *)
//
Line 465 ⟶ 539:
//
} (* end of [main0] *)
</syntaxhighlight>
</lang>
 
=={{header|AWK}}==
<syntaxhighlight lang="awk">
<lang AWK>
# syntax: GAWK -f STREAM_MERGE.AWK filename(s) >output
# handles 1 .. N files
Line 534 ⟶ 608:
errors++
}
</syntaxhighlight>
</lang>
 
=={{header|C}}==
<syntaxhighlight lang="c">/*
* Rosetta Code - stream merge in C.
*
* Two streams (text files) with integer numbers, C89, Visual Studio 2010.
*
*/
 
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
 
#define GET(N) { if(fscanf(f##N,"%d",&b##N ) != 1) f##N = NULL; }
#define PUT(N) { printf("%d\n", b##N); GET(N) }
 
void merge(FILE* f1, FILE* f2, FILE* out)
{
int b1;
int b2;
 
if(f1) GET(1)
if(f2) GET(2)
 
while ( f1 && f2 )
{
if ( b1 <= b2 ) PUT(1)
else PUT(2)
}
while (f1 ) PUT(1)
while (f2 ) PUT(2)
}
 
int main(int argc, char* argv[])
{
if ( argc < 3 || argc > 3 )
{
puts("streammerge filename1 filename2");
exit(EXIT_FAILURE);
}
else
merge(fopen(argv[1],"r"),fopen(argv[2],"r"),stdout);
 
return EXIT_SUCCESS;
}
</syntaxhighlight>
 
=={{header|C sharp|C#}}==
<syntaxhighlight lang="csharp">
using System;
using System.Collections.Generic;
using System.Linq;
 
namespace RosettaCode
{
static class StreamMerge
{
static IEnumerable<T> Merge2<T>(IEnumerable<T> source1, IEnumerable<T> source2) where T : IComparable
{
var q1 = new Queue<T>(source1);
var q2 = new Queue<T>(source2);
while (q1.Any() && q2.Any())
{
var c = q1.Peek().CompareTo(q2.Peek());
if (c <= 0) yield return q1.Dequeue(); else yield return q2.Dequeue();
}
while (q1.Any()) yield return q1.Dequeue();
while (q2.Any()) yield return q2.Dequeue();
}
 
static IEnumerable<T> MergeN<T>(params IEnumerable<T>[] sources) where T : IComparable
{
var queues = sources.Select(e => new Queue<T>(e)).Where(q => q.Any()).ToList();
var headComparer = Comparer<Queue<T>>.Create((x, y) => x.Peek().CompareTo(y.Peek()));
queues.Sort(headComparer);
while (queues.Any())
{
var q = queues.First();
queues.RemoveAt(0);
yield return q.Dequeue();
if (q.Any())
{
var index = queues.BinarySearch(q, headComparer);
queues.Insert(index < 0 ? ~index : index, q);
}
}
}
 
static void Main()
{
var a = new[] { 1, 4, 7, 10 };
var b = new[] { 2, 5, 8, 11 };
var c = new[] { 3, 6, 9, 12 };
 
foreach (var i in Merge2(a, b)) Console.Write($"{i} ");
Console.WriteLine();
 
foreach (var i in MergeN(a, b, c)) Console.Write($"{i} ");
Console.WriteLine();
}
}
}</syntaxhighlight>
{{out}}
<pre>1 2 4 5 7 8 10 11
1 2 3 4 5 6 7 8 9 10 11 12</pre>
 
=={{header|C++}}==
{{trans|C#}}
<syntaxhighlight lang="cpp">//#include <functional>
#include <iostream>
#include <vector>
 
template <typename C, typename A>
void merge2(const C& c1, const C& c2, const A& action) {
auto i1 = std::cbegin(c1);
auto i2 = std::cbegin(c2);
 
while (i1 != std::cend(c1) && i2 != std::cend(c2)) {
if (*i1 <= *i2) {
action(*i1);
i1 = std::next(i1);
} else {
action(*i2);
i2 = std::next(i2);
}
}
while (i1 != std::cend(c1)) {
action(*i1);
i1 = std::next(i1);
}
while (i2 != std::cend(c2)) {
action(*i2);
i2 = std::next(i2);
}
}
 
template <typename A, typename C>
void mergeN(const A& action, std::initializer_list<C> all) {
using I = typename C::const_iterator;
using R = std::pair<I, I>;
 
std::vector<R> vit;
for (auto& c : all) {
auto p = std::make_pair(std::cbegin(c), std::cend(c));
vit.push_back(p);
}
 
bool done;
R* least;
do {
done = true;
 
auto it = vit.begin();
auto end = vit.end();
least = nullptr;
 
// search for the first non-empty range to use for comparison
while (it != end && it->first == it->second) {
it++;
}
if (it != end) {
least = &(*it);
}
while (it != end) {
// search for the next non-empty range to use for comaprison
while (it != end && it->first == it->second) {
it++;
}
if (least != nullptr && it != end
&& it->first != it->second
&& *(it->first) < *(least->first)) {
// found a smaller value
least = &(*it);
}
if (it != end) {
it++;
}
}
if (least != nullptr && least->first != least->second) {
done = false;
action(*(least->first));
least->first = std::next(least->first);
}
} while (!done);
}
 
void display(int num) {
std::cout << num << ' ';
}
 
int main() {
std::vector<int> v1{ 0, 3, 6 };
std::vector<int> v2{ 1, 4, 7 };
std::vector<int> v3{ 2, 5, 8 };
 
merge2(v2, v1, display);
std::cout << '\n';
 
mergeN(display, { v1 });
std::cout << '\n';
 
mergeN(display, { v3, v2, v1 });
std::cout << '\n';
}</syntaxhighlight>
{{out}}
<pre>0 1 3 4 6 7
0 3 6
0 1 2 3 4 5 6 7 8</pre>
 
=={{header|D}}==
<syntaxhighlight lang="d">import std.range.primitives;
import std.stdio;
 
// An output range for writing the elements of the example ranges
struct OutputWriter {
void put(E)(E e) if (!isInputRange!E) {
stdout.write(e);
}
}
 
void main() {
import std.range : only;
merge2(OutputWriter(), only(1,3,5,7), only(2,4,6,8));
writeln("\n---------------");
mergeN(OutputWriter(), only(1,4,7), only(2,5,8), only(3,6,9));
writeln("\n---------------");
mergeN(OutputWriter(), only(1,2,3));
}
 
/+ Write the smallest element from r1 and r2 until both ranges are empty +/
void merge2(IN,OUT)(OUT sink, IN r1, IN r2)
if (isInputRange!IN && isOutputRange!(OUT, ElementType!IN)) {
import std.algorithm : copy;
 
while (!r1.empty && !r2.empty) {
auto a = r1.front;
auto b = r2.front;
if (a<b) {
sink.put(a);
r1.popFront;
} else {
sink.put(b);
r2.popFront;
}
}
copy(r1, sink);
copy(r2, sink);
}
 
/+ Write the smallest element from the sources until all ranges are empty +/
void mergeN(OUT,IN)(OUT sink, IN[] source ...)
if (isInputRange!IN && isOutputRange!(OUT, ElementType!IN)) {
ElementType!IN value;
bool done, hasValue;
int idx;
 
do {
hasValue = false;
done = true;
idx = -1;
 
foreach(i,r; source) {
if (!r.empty) {
if (hasValue) {
if (r.front < value) {
value = r.front;
idx = i;
}
} else {
hasValue = true;
value = r.front;
idx = i;
}
}
}
 
if (idx > -1) {
sink.put(source[idx].front);
source[idx].popFront;
done = false;
}
} while (!done);
}</syntaxhighlight>
 
{{out}}
<pre>12345678
---------------
123456789
---------------
123</pre>
 
=={{header|Elixir}}==
<langsyntaxhighlight lang="elixir">defmodule StreamMerge do
def merge2(file1, file2), do: mergeN([file1, file2])
Line 565 ⟶ 930:
StreamMerge.merge2("temp1.dat", "temp2.dat")
IO.puts "\nN-stream merge:"
StreamMerge.mergeN(filenames)</langsyntaxhighlight>
 
{{out}}
Line 615 ⟶ 980:
 
=={{header|Fortran}}==
This is a classic problem, but even so, Fortran does not supply a library routine for this. So...<langsyntaxhighlight Fortranlang="fortran"> SUBROUTINE FILEMERGE(N,INF,OUTF) !Merge multiple inputs into one output.
INTEGER N !The number of input files.
INTEGER INF(*) !Their unit numbers.
Line 682 ⟶ 1,047:
CALL FILEMERGE(MANY,FI,F) !E pluribus unum.
 
END !That was easy.</langsyntaxhighlight>
Obviously, there would be variations according to the nature of the data streams being merged, and whatever sort key was involved. For this example, input from disc files will do and the sort key is the entire record's text. This means there is no need to worry over the case where, having written a record from stream S and obtained the next record from stream S, it proves to have equal precedence with the waiting record for some other stream. Which now should take precedence? With entirely-equal records it obviously doesn't matter but if the sort key is only partial then different record content could be deemed equal and then a choice has an effect.
 
Line 692 ⟶ 1,057:
 
The source for subroutine GRAB is within subroutine FILEMERGE for the convenience in sharing and messing with variables important to both, but not to outsiders. This facility is standard in Algol-following languages but often omitted and was not added to Fortran until F90. In its absence, either more parameters are required for the separate routines, or there will be messing with COMMON storage areas.
 
=={{header|FreeBASIC}}==
{{trans|C++}}
<syntaxhighlight lang="vbnet">Sub Merge2(c1() As Integer, c2() As Integer)
Dim As Integer i1 = Lbound(c1)
Dim As Integer i2 = Lbound(c2)
While i1 <= Ubound(c1) And i2 <= Ubound(c2)
If c1(i1) <= c2(i2) Then
Print c1(i1);
i1 += 1
Else
Print c2(i2);
i2 += 1
End If
Wend
While i1 <= Ubound(c1)
Print c1(i1);
i1 += 1
Wend
While i2 <= Ubound(c2)
Print c2(i2);
i2 += 1
Wend
Print
End Sub
 
Sub MergeN(all() As Integer)
Dim As Integer i = Lbound(all)
While i <= Ubound(all)
Print all(i);
i += 1
Wend
Print
End Sub
 
Dim As Integer v1(2) = {0, 3, 6}
Dim As Integer v2(2) = {1, 4, 7}
Dim As Integer v3(2) = {2, 5, 8}
Merge2(v2(), v1())
MergeN(v1())
 
Dim As Integer all(8) = {v1(0), v2(0), v3(0), v1(1), v2(1), v3(1), v1(2), v2(2), v3(2)}
MergeN(all())
 
Sleep</syntaxhighlight>
{{out}}
<pre> 0 1 3 4 6 7
0 3 6
0 1 2 3 4 5 6 7 8</pre>
 
=={{header|Go}}==
'''Using standard library binary heap for mergeN:'''
<langsyntaxhighlight lang="go">package main
 
import (
Line 789 ⟶ 1,206:
}
}
}</langsyntaxhighlight>
{{out}}
<pre>
Line 796 ⟶ 1,213:
</pre>
'''MergeN using package from [[Fibonacci heap]] task:'''
<langsyntaxhighlight lang="go">package main
 
import (
Line 855 ⟶ 1,272:
}
}
}</langsyntaxhighlight>
{{out}}
<pre>
Line 861 ⟶ 1,278:
</pre>
 
== {{header|Haskell}} ==
 
There is no built-in iterator or stream type for file operations in Haskell. But several such libraries exist.
Line 867 ⟶ 1,284:
=== conduit ===
 
<langsyntaxhighlight lang="haskell">-- stack runhaskell --package=conduit-extra --package=conduit-merge
 
import Control.Monad.Trans.Resource (runResourceT)
Line 885 ⟶ 1,302:
runResourceT $ mergeSources inputs $$ sinkStdoutLn
where
sinkStdoutLn = Conduit.map (`BS.snoc` '\n') =$= sinkHandle stdout</langsyntaxhighlight>
 
See implementation in https://github.com/cblp/conduit-merge/blob/master/src/Data/Conduit/Merge.hs
Line 891 ⟶ 1,308:
=== pipes ===
 
<langsyntaxhighlight lang="haskell">-- stack runhaskell --package=pipes-safe --package=pipes-interleave
 
import Pipes (runEffect, (>->))
Line 905 ⟶ 1,322:
sourceFileNames <- getArgs
let sources = map readFile sourceFileNames
runSafeT . runEffect $ interleave compare sources >-> stdoutLn</langsyntaxhighlight>
 
See implementation in https://github.com/bgamari/pipes-interleave/blob/master/Pipes/Interleave.hs
 
=={{header|Java}}==
<syntaxhighlight lang="java">import java.util.Iterator;
import java.util.List;
import java.util.Objects;
 
public class StreamMerge {
private static <T extends Comparable<T>> void merge2(Iterator<T> i1, Iterator<T> i2) {
T a = null, b = null;
 
while (i1.hasNext() || i2.hasNext()) {
if (null == a && i1.hasNext()) {
a = i1.next();
}
if (null == b && i2.hasNext()) {
b = i2.next();
}
 
if (null != a) {
if (null != b) {
if (a.compareTo(b) < 0) {
System.out.print(a);
a = null;
} else {
System.out.print(b);
b = null;
}
} else {
System.out.print(a);
a = null;
}
} else if (null != b) {
System.out.print(b);
b = null;
}
}
 
if (null != a) {
System.out.print(a);
}
if (null != b) {
System.out.print(b);
}
}
 
@SuppressWarnings("unchecked")
@SafeVarargs
private static <T extends Comparable<T>> void mergeN(Iterator<T>... iter) {
Objects.requireNonNull(iter);
if (iter.length == 0) {
throw new IllegalArgumentException("Must have at least one iterator");
}
 
Object[] pa = new Object[iter.length];
boolean done;
 
do {
done = true;
 
for (int i = 0; i < iter.length; i++) {
Iterator<T> t = iter[i];
if (null == pa[i] && t.hasNext()) {
pa[i] = t.next();
}
}
 
T min = null;
int idx = -1;
for (int i = 0; i < pa.length; ++i) {
T t = (T) pa[i];
if (null != t) {
if (null == min) {
min = t;
idx = i;
done = false;
} else if (t.compareTo(min) < 0) {
min = t;
idx = i;
done = false;
}
}
}
if (idx != -1) {
System.out.print(min);
pa[idx] = null;
}
} while (!done);
}
 
public static void main(String[] args) {
List<Integer> l1 = List.of(1, 4, 7, 10);
List<Integer> l2 = List.of(2, 5, 8, 11);
List<Integer> l3 = List.of(3, 6, 9, 12);
 
merge2(l1.iterator(), l2.iterator());
System.out.println();
 
mergeN(l1.iterator(), l2.iterator(), l3.iterator());
System.out.println();
System.out.flush();
}
}</syntaxhighlight>
{{out}}
<pre>1245781011
123456789101112</pre>
 
=={{header|Julia}}==
{{trans|C}}
The IOStream type in Julia encompasses any data stream, including file I/O and TCP/IP. The IOBuffer used here maps a stream to a buffer in memory, and so allows an easy simulation of two streams without opening files.
<syntaxhighlight lang="julia">
function merge(stream1, stream2, T=Char)
if !eof(stream1) && !eof(stream2)
b1 = read(stream1, T)
b2 = read(stream2, T)
while !eof(stream1) && !eof(stream2)
if b1 <= b2
print(b1)
if !eof(stream1)
b1 = read(stream1, T)
end
else
print(b2)
if !eof(stream2)
b2 = read(stream2, T)
end
end
end
while !eof(stream1)
print(b1)
b1 = read(stream1, T)
end
print(b1)
while !eof(stream2)
print(b2)
b2 = read(stream2, T)
end
print(b2)
end
end
 
const halpha1 = "acegikmoqsuwy"
const halpha2 = "bdfhjlnprtvxz"
const buf1 = IOBuffer(halpha1)
const buf2 = IOBuffer(halpha2)
 
merge(buf1, buf2, Char)
println("\nDone.")
 
</syntaxhighlight>{{output}}<pre>
abcdefghijklmnopqrstuvwyxz
Done.
</pre>
 
=={{header|Kotlin}}==
Uses the same data as the REXX entry. As Kotlin lacks a Heap class, when merging N files, we use a nullable MutableList instead. All comparisons are text based even when the files contain nothing but numbers.
<syntaxhighlight lang="scala">// version 1.2.21
 
import java.io.File
 
fun merge2(inputFile1: String, inputFile2: String, outputFile: String) {
val file1 = File(inputFile1)
val file2 = File(inputFile2)
require(file1.exists() && file2.exists()) { "Both input files must exist" }
val reader1 = file1.bufferedReader()
val reader2 = file2.bufferedReader()
val writer = File(outputFile).printWriter()
var line1 = reader1.readLine()
var line2 = reader2.readLine()
while (line1 != null && line2 != null) {
if (line1 <= line2) {
writer.println(line1)
line1 = reader1.readLine()
}
else {
writer.println(line2)
line2 = reader2.readLine()
}
}
while (line1 != null) {
writer.println(line1)
line1 = reader1.readLine()
}
while (line2 != null) {
writer.println(line2)
line2 = reader2.readLine()
}
reader1.close()
reader2.close()
writer.close()
}
 
fun mergeN(inputFiles: List<String>, outputFile: String) {
val files = inputFiles.map { File(it) }
require(files.all { it.exists() }) { "All input files must exist" }
val readers = files.map { it.bufferedReader() }
val writer = File(outputFile).printWriter()
var lines = readers.map { it.readLine() }.toMutableList()
while (lines.any { it != null }) {
val line = lines.filterNotNull().min()
val index = lines.indexOf(line)
writer.println(line)
lines[index] = readers[index].readLine()
}
readers.forEach { it.close() }
writer.close()
}
 
fun main(args:Array<String>) {
val files = listOf("merge1.txt", "merge2.txt", "merge3.txt", "merge4.txt")
merge2(files[0], files[1], "merged2.txt")
mergeN(files, "mergedN.txt")
// check it worked
println(File("merged2.txt").readText())
println(File("mergedN.txt").readText())
}</syntaxhighlight>
 
{{out}}
<pre>
1
17
19
33
500
8
 
1
17
19
1999
2999
2e3
3000
33
3999
500
8
</pre>
=={{header|Nim}}==
===Merge two streams===
Optimized for clarity and simplicity, not performance.
assumes two files containing sorted integers separated by newlines
<syntaxhighlight lang="nim">import streams,strutils
let
stream1 = newFileStream("file1")
stream2 = newFileStream("file2")
while not stream1.atEnd and not stream2.atEnd:
echo (if stream1.peekLine.parseInt > stream2.peekLine.parseInt: stream2.readLine else: stream1.readLine)
 
for line in stream1.lines:
echo line
for line in stream2.lines:
echo line</syntaxhighlight>
 
===Merge N streams===
{{trans|Phix}}
Of course, as Phix and Nim are very different languages, the code is quite different, but as Phix, we use a priority queue (which is provided by the standard module <code>heapqueue</code>. We work with files built from the “Data” constant, but we destroy them after usage. We have also put the whole merging code in an procedure.
 
<syntaxhighlight lang="nim">import heapqueue, os, sequtils, streams
 
type
Source = tuple[stream: Stream; line: string]
SourceHeap = HeapQueue[Source]
 
 
# Comparison of sources. Needed for the heap to sort the sources by line contents.
proc `<`(a, b: Source): bool = a.line < b.line
 
 
proc add(heap: var SourceHeap; stream: Stream) =
## Add a stream to the heap.
if stream.atEnd:
stream.close()
else:
heap.push((stream, stream.readLine()))
 
 
proc merge(inStreams: seq[Stream]; outStream: Stream) =
## Merge the input streams into an output stream.
 
# Initialize the heap.
var heap: SourceHeap
for stream in inStreams:
heap.add(stream)
 
# Merging loop.
while heap.len > 0:
let (stream, line) = heap.pop()
outStream.writeLine line
heap.add(stream)
 
 
when isMainModule:
 
const
Data = ["Line 001\nLine 008\nLine 017\n",
"Line 019\nLine 033\nLine 044\nLine 055\n",
"Line 019\nLine 029\nLine 039\n",
"Line 023\nLine 030\n"]
Filenames = ["file1.txt", "file2.txt", "file3.txt", "file4.txt"]
 
# Create files.
for i, name in Filenames:
writeFile(name, Data[i])
 
# Create input and output streams.
let inStreams = Filenames.mapIt(Stream(newFileStream(it)))
let outStream = Stream(newFileStream(stdout))
 
# Merge the streams.
merge(inStreams, outStream)
 
# Clean-up: delete the files.
for name in Filenames:
removeFile(name)</syntaxhighlight>
 
{{out}}
<pre>Line 001
Line 008
Line 017
Line 019
Line 019
Line 023
Line 029
Line 030
Line 033
Line 039
Line 044
Line 055</pre>
 
=={{header|Perl}}==
We make use of an iterator interface which String::Tokenizer provides. Credit: we obtained all the sample text from http://www.lipsum.com/.
<langsyntaxhighlight lang="perl">use strict;
use warnings;
use English;
use String::Tokenizer;
Line 1,003 ⟶ 1,749:
my $iter = $iters[$i];
$iter->hasNextToken() or die "Each stream must have >= 1 element";
$heap->insert([$iter->nextToken() . q{ }, $i]);
}
$heap->count == $N or die "Problem with initial population of heap";
while (1) {
my ($token, $iter_idx) = @{ $heap->extract_top };
print $token, q{ };
# Attempt to read the next element from the same iterator where we
# obtained the element we just extracted.
my $to_insert = _fetch_next_element($iter_idx, $N, @iters);
if (! $to_insert) {
print join(q{ }'', map {$ARG->[0]} $heap->extract_all), "\n\n";
last;
}
Line 1,030 ⟶ 1,776:
my $iter = $iters[$iter_idx];
if ($iter->hasNextToken()) {
return [$iter->nextToken() . q{ }, $iter_idx];
}
}
# At this point every iterator has been exhausted.
return;
}</langsyntaxhighlight>
{{out}}
<pre>Merge of 2 streams:
Merge of 2 streams:
In Integer luctus odio nulla, ut finibus elit aliquet in. In auctor vel neque ligula. Etiam a ipsum a leo eleifend vitae purus quis tristique. Mauris sed erat pulvinar, venenatis lectus auctor, malesuada neque. Integer a hendrerit tortor. Suspendisse aliquet pellentesque lorem, nec tincidunt arcu aliquet non. Phasellus eu diam massa. Integer vitae viverra sit amet ac arcu. Suspendisse odio libero, ullamcorper eu sem vitae, gravida dignissim ipsum. Aenean tincidunt commodo feugiat. Nunc viverra dolor a tincidunt porta. Ut malesuada quis mauris eget vestibulum. Fusce sit amet libero id augue mattis auctor et sit amet ligula. volutpat augue. Nulla condimentum consectetur ante, ut consequat lectus suscipit eget.
 
Merge of 6 streams:
Curabitur Donec In In Integer Suspendisse elementum hendrerit eleifend luctus mi nec enim eu nec nisi maximus nunc ex, ac odio nulla, pulvinar mauris finibus sed. Ut non ex sed sit amet suscipit rutrum non sem. Donec lobortis nulla et rutrum bibendum. Duis tortor ultricies feugiat non at eros. Donec et scelerisque est. In ultricies. Vestibulum euismod, tellus sit amet eleifend ultrices, urna nulla. Duis nec consectetur lacus, et ut finibus elit aliquet in. In auctor varius, tellus in commodo gravida, lorem neque finibus quam, sagittis elementum leo mauris sit amet justo. Sed vel neque ligula. Etiam a ipsum a leo eleifend velit nisi dignissim lectus, non vestibulum fringilla metus eget varius. Aenean fringilla pellentesque massa, non ullamcorper mi commodo non. Sed aliquam molestie congue. Nunc lobortis turpis at nunc lacinia, id laoreet ipsum bibendum. ex. Aliquam lobortis tristique hendrerit. Suspendisse vestibulum velit eget sapien bibendum, sit amet porta lorem fringilla. Morbi bibendum in turpis ac blandit. Mauris semper nibh nec dignissim dapibus. Proin sagittis lacus est. sit amet ac arcu. Suspendisse odio libero, ullamcorper eu sem vestibulum sem nisi sed mi. Nulla scelerisque ut purus sed ultricies. Donec pulvinar eleifend malesuada. In vitae purus quis tristique. Mauris sed erat pulvinar, venenatis lectus auctor, malesuada neque. Integer a hendrerit tortor. Suspendisse aliquet pellentesque lorem, nec tincidunt arcu aliquet non. Phasellus eu diam massa. Integer vitae vitae, gravida dignissim ipsum. Aenean tincidunt commodo feugiat. Nunc viverra vehicula lorem id gravida. Pellentesque at ligula lorem. Cras gravida accumsan lacus sit amet tincidunt. Curabitur quam nisi, viverra dolor a tincidunt porta. Ut malesuada quis mauris eget vestibulum. Fusce sit amet libero id augue mattis auctor et sit amet ligula. augue. Nulla condimentum consectetur ante, ut consequat lectus suscipit eget. faucibus enim a luctus. Vivamus tellus erat, congue quis quam in, lobortis varius mi. Nulla ante orci, porttitor id dui ac, iaculis consequat ligula. vel nulla vel, rhoncus facilisis massa. Aliquam erat viverra viverra viverra viverra volutpat volutpat.</pre>
</pre>
 
=={{header|Perl 6Phix}}==
Using a priority queue
<lang perl6>sub merge_streams ( @streams ) {
<!--<syntaxhighlight lang="phix">(notonline)-->
my @s = @streams.map({ hash( STREAM => $_, HEAD => .get ) })\
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- file i/o</span>
.grep({ .<HEAD>.defined });
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">/</span><span style="color: #000000;">pqueue</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
 
return gather while @s {
<span style="color: #008080;">procedure</span> <span style="color: #000000;">add</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">fn</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pq</span><span style="color: #0000FF;">)</span>
my $h = @s.min: *.<HEAD>;
<span style="color: #004080;">object</span> <span style="color: #000000;">line</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">gets</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fn</span><span style="color: #0000FF;">)</span>
take $h<HEAD>;
<span style="color: #008080;">if</span> <span style="color: #000000;">line</span><span style="color: #0000FF;">=-</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
$h<HEAD> = $h<STREAM>.get
<span style="color: #7060A8;">close</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fn</span><span style="color: #0000FF;">)</span>
orelse @s .= grep( { $_ !=== $h } );
<span style="color: #008080;">else</span>
}
<span style="color: #7060A8;">pq_add</span><span style="color: #0000FF;">({</span><span style="color: #000000;">fn</span><span style="color: #0000FF;">,</span><span style="color: #000000;">line</span><span style="color: #0000FF;">},</span> <span style="color: #000000;">pq</span><span style="color: #0000FF;">)</span>
}
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
 
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
say merge_streams([ @*ARGS».&open ]);</lang>
<span style="color: #000080;font-style:italic;">-- setup (optional/remove if files already exist)</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">data</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"Line 001\nLine 008\nLine 017\n"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"Line 019\nLine 033\nLine 044\nLine 055\n"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"Line 019\nLine 029\nLine 039\n"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"Line 023\nLine 030\n"</span><span style="color: #0000FF;">},</span>
<span style="color: #000000;">filenames</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"file1.txt"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"file2.txt"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"file3.txt"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"file4.txt"</span><span style="color: #0000FF;">}</span>
<span style="color: #000080;font-style:italic;">-- (or command_line()[3..$] if you prefer)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">filenames</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">fn</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">open</span><span style="color: #0000FF;">(</span><span style="color: #000000;">filenames</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span> <span style="color: #008000;">"w"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">fn</span><span style="color: #0000FF;"><</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"cannot open file"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fn</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">data</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #7060A8;">close</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fn</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000080;font-style:italic;">-- initilisation</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">pq</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">pq_new</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">filenames</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">fn</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">open</span><span style="color: #0000FF;">(</span><span style="color: #000000;">filenames</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span> <span style="color: #008000;">"r"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">fn</span><span style="color: #0000FF;"><</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"cannot open file"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">add</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fn</span><span style="color: #0000FF;">,</span><span style="color: #000000;">pq</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #000080;font-style:italic;">-- main loop</span>
<span style="color: #008080;">while</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">pq_empty</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pq</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #0000FF;">{</span><span style="color: #004080;">integer</span> <span style="color: #000000;">fn</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">string</span> <span style="color: #000000;">line</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">pq_pop</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pq</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">line</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">add</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fn</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pq</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #7060A8;">pq_destroy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pq</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- cleanup (optional/remove if files already exist)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">filenames</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">delete_file</span><span style="color: #0000FF;">(</span><span style="color: #000000;">filenames</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
<pre>
Line 001
Line 008
Line 017
Line 019
Line 019
Line 023
Line 029
Line 030
Line 033
Line 039
Line 044
Line 055
</pre>
 
=={{header|PicoLisp}}==
<langsyntaxhighlight PicoLisplang="picolisp">(de streamMerge @
(let Heap
(make
Line 1,074 ⟶ 1,870:
(if (in (cdar Heap) (read))
(set (car Heap) @)
(close (cdr (pop 'Heap))) ) ) ) ) )</langsyntaxhighlight>
<pre>$ cat a
3 14 15
Line 1,086 ⟶ 1,882:
2 3 5 7</pre>
Test:
<langsyntaxhighlight PicoLisplang="picolisp">(test (2 3 14 15 17 18)
(streamMerge
(open "a")
Line 1,096 ⟶ 1,892:
(open "b")
(open "c")
(open "d") ) )</langsyntaxhighlight>
'streamMerge' works with non-numeric data as well, and also - instead of calling
'open' on a file or named pipe - with the results of 'connect' or 'listen' (i.e.
sockets).
 
== {{header|Python}} ==
 
Built-in function <code>open</code> opens a file for reading and returns a line-by-line iterator (stream) over the file.
Line 1,107 ⟶ 1,903:
There exists a standard library function <code>heapq.merge</code> that takes any number of sorted stream iterators and merges them into one sorted iterator, using a [[heap]].
 
<langsyntaxhighlight lang="python">import heapq
import sys
 
sources = sys.argv[1:]
for item in heapq.merge(open(source) for source in sources):
print(item)</langsyntaxhighlight>
 
=={{header|Racket}}==
 
<langsyntaxhighlight lang="racket">;; This module produces a sequence that merges streams in order (by <)
#lang racket/base
(require racket/stream)
Line 1,188 ⟶ 1,984:
'(1 2 3 4 5 6 7 8 9 10))
(check-equal? (for/list ((i (merge-sequences/< '(2 4 6 7 8 9 10) '(1 3 5)))) i)
'(1 2 3 4 5 6 7 8 9 10)))</langsyntaxhighlight>
 
{{out}}
Line 1,202 ⟶ 1,998:
monkey</pre>
 
== {{header|REXX}} ==
===version 1===
<syntaxhighlight lang ="rexx">/******* REXX ***************************************************************
* Merge 1.txt ... n.txt into mall.txt
* 1.txt 2.txt 3.txt 4.txt
* 1 19 1999 2e3
* 17 33 2999 3000
* 8 500 3999 RC task STREAM MERGE
**********************************************************************/
done.=0 /* done.i=1 indicates file exhausted */
n=4
p.='' /* for test of sort error */
high='ffff'x
Do i=1 By 1 /* check files for existence */
p.=''
Do i=1 To n
f.i=i'.txt'
If lines(f.i)=0 Then Leave
Call get i
Call get i /* and read first line of each*/
End
n=i-1 /* we have n input files */
Do Forever
done.0=n /* and all must be used */
min=high
say n 'Input files'
oid='all.txt'
If lines(oid)>0 Then Do /* output file exists */
Call lineout oid
Do Until wordpos(answer,'Y N')>0
Say 'file' oid 'exists. May it be replaced?'
Pull answer
End
If answer='Y' Then
'erase' oid
Else Do
Say 'Ok, we give up'
Exit
End
End
say oid 'is the output file' /* we'll create it now */
Do Until done.0=0
imin=0 /* index of next source */
Do i=1 To n
If xdone.i<<min=0 Then Do /* avoidfile i still in use numerical comparison */
If imin=0 Then Do /* it's the first in this loop*/
imin=i
imin=i /* next source */
min=x.i
min=x.i /* element to be used */
End
Else Do /* not the first */
If x.i<<min Then Do /* avoid numerical comparison */
imin=i /* next source */
min=x.i /* element to be used */
End
End
End
End
If imin<>0 Then Do /* found next source */
If min<<high Then Do
Call o x.imin /* use its element */
Call get imin /* get next element */
/* or set done.imin */
End
Else Do
Call lineout oid
Leave
End
Else /* no more elements */
Call lineout oid /* close output file */
End
'type' oid
Exit
 
get: Procedure Expose f. x. high p.
get: Procedure Expose f. x. p. done.
/*********************************************************************
* Get next element from file ii or set done.ii=1 if file is exhausted
*********************************************************************/
Parse Arg ii
If lines(f.ii)=0 Then Do /* file ii is exhausted */
done.ii=1 /* mark it as done */
x.ii=high
done.0=done.0-1 /* reduce number of files tbd*/
Else Do
End
x.ii=linein(f.ii)
Else Do /* there's more in file ii */
If x.ii<<p.ii Then Do
x.ii=linein(f.ii) /* get next element (line) */
If x.ii<<p.ii Then Do /* smaller than previous */
Say 'Input file' f.ii 'is not sorted ascendingly'
Say p.ii 'precedes' x.ii /* tell the user */
Exit /* and give up */
Exit
End
p.ii=x.ii /* remember the element */
p.ii=x.ii
End
Return
 
o: Say arg(1)
o: Return lineout(oid,arg(1))</langsyntaxhighlight>
{{out}}
<pre>1
Line 1,266 ⟶ 2,094:
 
===version 2===
This REXX version reads &nbsp; (in numerical order) &nbsp; ''any'' &nbsp; number of input files in the form of: &nbsp; &nbsp; <big> nnn.TXT </big> &nbsp; &nbsp; and
<br>and stops reading subsequent &nbsp; ''new'' &nbsp; input files when it encounters aan input file that doesn't exist &nbsp; (or is empty).
 
This REXX program will execute correctly when executed multiple times.
 
The input files would/should be named: &nbsp; &nbsp; '''1.TXT &nbsp; &nbsp; 2.TXT &nbsp; &nbsp; 3.TXT &nbsp; &nbsp; 4.TXT &nbsp; &nbsp; ···'''
 
No &nbsp; ''heap'' &nbsp; is needed to keep track of which record was written, nor needs replenishing from its input file.
<langsyntaxhighlight lang="rexx">/*REXX pgm reads sorted files (1.TXT, 2.TXT, ···), and writes sorted data ───► ALL.TXT */
$@.=copies('FFff'x, 1e51e4); call lineout 'ALL.TXT',,1 /*no value should be larger than this. */
@.=$ do n=1 until @.n==@.; call rdr n; end /*theread defaultany valuenumber forof theappropriate @ arrayfiles. */
n=n-1 do n=1 until @.n==$; call rdr n; end /*readfix anyN number'cause read ofa appropriatenon─existent files.file*/
do forever; y=@.; #=0 /*find [↑]the lowest readvalue 'tilfor a non─existentN file. values.*/
do forever;do k=1 for y=$n; if #@.k==0 @. then call rdr k /*findNot thedefined? lowest valueThen forread a Nfile values.record*/
if do @.k=1 for n-1 <<y then do; y=@.k; #=k; end /*traipseLowest throughso thefar? stemmed Mark @this as arrayminimum.*/
end if @./*k==$*/ then call rdr k /*Not defined?[↑] note use Thenof read a<< file(exact recordcompare)*/
if @.k<<y then do; y=@.k; #=k; end /*Lowest so far? Then mark this as min*/
end /*k*/
if #==0 then exit /*stick a fork in it, we're all done. */
call lineout 'ALL.TXT', @.#; say @.# /*outputwrite value to a file; also display.*/
call rdr # /*repopulatere─populate thisa file'svalue inputfrom value.the # file. */
end /*forever*/ /*keep reading/merging until exhausted.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
rdr: parse arg z; @.z=$ @.; f= z'.TXT'; if lines(f)\==0 then @.z= linein(f); return</langsyntaxhighlight>
'''{{out|output''' |text=&nbsp; is the same as the 1<sup>st</sup> REXX version when using identical input files), &nbsp; except the output file is named &nbsp; '''ALL.TXT'''}} <br><br>
 
=={{header|Raku}}==
(formerly Perl 6)
{{works with|Rakudo|2018.02}}
 
<syntaxhighlight lang="raku" line>sub merge_streams ( @streams ) {
my @s = @streams.map({ hash( STREAM => $_, HEAD => .get ) })\
.grep({ .<HEAD>.defined });
 
return gather while @s {
my $h = @s.min: *.<HEAD>;
take $h<HEAD>;
$h<HEAD> := $h<STREAM>.get
orelse @s .= grep( { $_ !=== $h } );
}
}
 
say merge_streams([ @*ARGS».&open ]);</syntaxhighlight>
 
=={{header|Ruby}}==
<langsyntaxhighlight lang="ruby">def stream_merge(*files)
fio = files.map{|fname| open(fname)}
merge(fio.map{|io| [io, io.gets]})
Line 1,313 ⟶ 2,161:
puts "#{fname}: #{data}"
end
stream_merge(*files)</langsyntaxhighlight>
 
{{out}}
Line 1,341 ⟶ 2,189:
30
</pre>
 
=={{header|Scala}}==
<syntaxhighlight lang="scala">def mergeN[A : Ordering](is: Iterator[A]*): Iterator[A] = is.reduce((a, b) => merge2(a, b))
 
def merge2[A : Ordering](i1: Iterator[A], i2: Iterator[A]): Iterator[A] = {
merge2Buffered(i1.buffered, i2.buffered)
}
 
def merge2Buffered[A](i1: BufferedIterator[A], i2: BufferedIterator[A])(implicit ord: Ordering[A]): Iterator[A] = {
if (!i1.hasNext) {
i2
} else if (!i2.hasNext) {
i1
} else {
val nextHead = if (ord.lt(i1.head, i2.head)) {
Iterator.single(i1.next)
} else {
Iterator.single(i2.next)
}
nextHead ++ merge2Buffered(i1, i2)
}
}</syntaxhighlight>
 
Example usage, demonstrating lazyness:
 
<syntaxhighlight lang="scala">val i1 = Iterator.tabulate(5) { i =>
val x = i * 3
println(s"generating $x")
x
}
 
val i2 = Iterator.tabulate(5) { i =>
val x = i * 3 + 1
println(s"generating $x")
x
}
 
val i3 = Iterator.tabulate(5) { i =>
val x = i * 3 + 2
println(s"generating $x")
x
}
 
val merged = mergeN(i1, i2, i3)
 
while (merged.hasNext) {
val x = merged.next
println(s"output: $x")
}</syntaxhighlight>
 
{{out}}
<pre>generating 0
generating 1
generating 2
output: 0
generating 3
output: 1
generating 4
output: 2
generating 5
output: 3
generating 6
output: 4
generating 7
output: 5
generating 8
output: 6
generating 9
output: 7
generating 10
output: 8
generating 11
output: 9
generating 12
output: 10
generating 13
output: 11
generating 14
output: 12
output: 13
output: 14</pre>
 
=={{header|Sidef}}==
{{trans|Perl 6Raku}}
<langsyntaxhighlight lang="ruby">func merge_streams(streams) {
var s = streams.map { |stream|
Pair(stream, stream.readline)
Line 1,358 ⟶ 2,287:
}
 
say merge_streams(ARGV.map {|f| File(f).open_r }).join("\n")</langsyntaxhighlight>
 
=={{header|Tcl}}==
Line 1,365 ⟶ 2,294:
A careful reader will notice that '''$peeks''' is treated alternately as a dictionary ('''dict set''', '''dict get''') and as a list ('''lsort''', '''lassign'''), exploiting the fact that dictionaries are simply lists of even length. For large dictionaries this would not be recommended, as it causes [https://wiki.tcl.tk/3033 "shimmering"], but in this example the impact is too small to matter.
 
<langsyntaxhighlight Tcllang="tcl">#!/usr/bin/env tclsh
proc merge {args} {
set peeks {}
Line 1,385 ⟶ 2,314:
 
merge {*}[lmap f $::argv {open $f r}]
</syntaxhighlight>
</lang>
 
== {{header|UNIX Shell}} ==
 
sort --merge source1 source2 sourceN > sink
 
== {{header|zklWren}} ==
{{trans|Kotlin}}
{{libheader|Wren-ioutil}}
{{libheader|Wren-str}}
{{libheader|Wren-seq}}
No Heap class, so we use a List. Comparisons are text based even for numbers.
<syntaxhighlight lang="wren">import "io" for File
import "./ioutil" for FileUtil
import "./str" for Str
import "./seq" for Lst
 
var merge2 = Fn.new { |inputFile1, inputFile2, outputFile|
// Note that the FileUtil.readEachLine method checks the file exists and closes it
// when there are no more lines to read.
var reader1 = Fiber.new(FileUtil.readEachLine(inputFile1))
var reader2 = Fiber.new(FileUtil.readEachLine(inputFile2))
var writer = File.create(outputFile)
var line1 = reader1.call()
var line2 = reader2.call()
while (line1 && line2) {
if (Str.le(line1, line2)) {
writer.writeBytes(line1 + "\n")
line1 = reader1.call()
} else {
writer.writeBytes(line2 + "\n")
line2 = reader2.call()
}
}
while (line1) {
writer.writeBytes(line1 + "\n")
line1 = reader1.call()
}
while (line2) {
writer.writeBytes(line2 + "\n")
line2 = reader2.call()
}
writer.close()
}
 
var mergeN = Fn.new { |inputFiles, outputFile|
var readers = inputFiles.map { |f| Fiber.new(FileUtil.readEachLine(f)) }.toList
var writer = File.create(outputFile)
var lines = readers.map { |reader| reader.call() }.toList
while (lines.any { |line| line }) {
var line = lines.where { |line| line }.reduce() { |acc, s| Str.lt(s, acc) ? s : acc }
var index = Lst.indexOf(lines, line)
writer.writeBytes(line + "\n")
lines[index] = readers[index].call()
}
writer.close()
}
 
var files = ["merge1.txt", "merge2.txt", "merge3.txt", "merge4.txt"]
merge2.call(files[0], files[1], "merged2.txt")
mergeN.call(files, "mergedN.txt")
// check it worked
System.print(File.read("merged2.txt"))
System.print(File.read("mergedN.txt"))</syntaxhighlight>
 
{{out}}
<pre>
1
17
19
33
500
8
 
1
17
19
1999
2999
2e3
3000
33
3999
500
8
</pre>
 
=={{header|zkl}}==
This solution uses iterators, doesn't care where the streams orginate and only keeps the head of the stream on hand.
<langsyntaxhighlight lang="zkl">fcn mergeStreams(s1,s2,etc){ //-->Walker
streams:=vm.arglist.pump(List(),fcn(s){ // prime and prune
if( (w:=s.walker())._next() ) return(w);
Line 1,406 ⟶ 2,416:
v
}.fp(streams));
}</langsyntaxhighlight>
Using infinite streams:
<langsyntaxhighlight lang="zkl">w:=mergeStreams([0..],[2..*,2],[3..*,3],T(5));
w.walk(20).println();</langsyntaxhighlight>
{{out}}
<pre>
Line 1,415 ⟶ 2,425:
</pre>
Using files:
<langsyntaxhighlight lang="zkl">w:=mergeStreams(File("unixdict.txt"),File("2hkprimes.txt"),File("/dev/null"));
do(10){ w.read().print() }</langsyntaxhighlight>
{{out}}
<pre>
Line 1,431 ⟶ 2,441:
</pre>
Using the above example to squirt the merged stream to a file:
<langsyntaxhighlight lang="zkl">mergeStreams(File("unixdict.txt"),File("2hkprimes.txt"),File("/dev/null"))
.pump(File("foo.txt","w"));</langsyntaxhighlight>
{{out}}
<pre>
2,122

edits