Task: Stream merge



C#: 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();
        }
    }
}









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;