"Connectivité" du code à l'aide de l'exemple d'un générateur de tracés ASCII, un utilitaire pour les opérations avec intervalles et démo sur Blazor WebAssembly

Travailler avec des rĂšgles peut ĂȘtre dĂ©routant. Imaginez que vous ayez une application de comptabilitĂ©. Et vous devez obtenir les pĂ©riodes pendant lesquelles l'employĂ© a travaillĂ© selon l'horaire «2 Ă  2» avant l'indexation salariale. Dans ce cas, il est nĂ©cessaire de prendre en compte les vacances, les changements d'horaires de travail, les licenciements / rĂ©intĂ©grations, les mutations vers d'autres services et d'autres activitĂ©s du personnel. Ces informations sont stockĂ©es sous forme de commandes, qui ont une «date de dĂ©but» et une «date de fin», c'est-Ă -dire. vous avez des pĂ©riodes de temps pour opĂ©rer.





Par exemple, recherchez l'intersection de tous les intervalles:





          2              5         7         9        
          |--------------|         |---------|        
0              3    4         6    7              10  
|--------------|    |---------|    |--------------|   
     1              4    5              8             
     |--------------|    |--------------|             
Result
          2    3                   7    8             
          |----|                   |----|

//       
      
      



L'utilitaire annoncé est destiné à résoudre de tels problÚmes.





Travailler avec des périodes dans la représentation visuelle est beaucoup plus facile, donc pour les tests (il y a aussi un ensemble de tests) et la documentation, j'ai fait une génération simple d'images ASCII, comme indiqué ci-dessus.





, ASCII y=f(x).





1. ASCII Blazor WebAssembly. y x.





IntervalUtility

GitHub, NuGet, demo Blazor WebAssembly.





. GitHub.









var arrayOfArrays = new[] {
    new[] { new Interval<int>(2,5), new Interval<int>(7, 9) },
    new[] { new Interval<int>(0,3), new Interval<int>(4, 6), 
            new Interval<int>(7, 10) },
    new[] { new Interval<int>(1,4), new Interval<int>(5, 8) },
};

var intervalUtil = new IntervalUtil();
var res = intervalUtil.Intersections(arrayOfArrays);
// => [2,3], [7,8]


          2              5         7         9       
          |--------------|         |---------|       
0              3    4         6    7              10 
|--------------|    |---------|    |--------------|  
     1              4    5              8            
     |--------------|    |--------------|            
Result
          2    3                   7    8            
          |----|                   |----| 
      
      







var intervalsA = new[] { new Interval<int>(1, 5), new Interval<int>(7, 10) };
var intervalsB = new[] { new Interval<int>(0, 2), new Interval<int>(3, 5), 
                         new Interval<int>(8, 9) };

var intervalUtil = new IntervalUtil();
var res = intervalUtil.Exclude(intervalsA, intervalsB);
// => [2,3], [7,8], [9,10]


     1                   5         7              10
     |-------------------|         |--------------|
0         2    3         5              8    9
|---------|    |---------|              |----|
Result
          2    3                   7    8    9    10
          |----|                   |----|    |----|
      
      



. ASCII .





ASCII

demo Blazor WebAssembly.





“ ”. /. , “ ”, , “ ” - .





: “ ASCII” ,

( ). “” . . - . . - .





, :





  • ,





  • , , .





/ , .





public class DrawerProcessor {
    public void Draw(
        //          
        Func<int, int, DrawerBlock> blockDraw, 
        //     
        Action<string, bool> onBlockDraw) {

        int row = 0;
        int blockIndex = 0;

        var done = false;
        while (!done) {
            var block = blockDraw(row, blockIndex);

            switch (block.Command) {
                case DrawerCommand.Continue:
                    blockIndex = blockIndex + block.Displacement;
                    break;
                case DrawerCommand.NewLine:
                    row = row + 1;
                    blockIndex = 0;
                    break;
                case DrawerCommand.End:
                    done = true;
                    break;
            }

            onBlockDraw(block.Value, done);
        }
    }
}

public class DrawerBlock {
    public string Value { get; set; }
    public DrawerCommand Command { get; set; }

    //   ,
    // Value      
    //   Value         2
    public int Displacement { get; set; } = 1;
}
      
      



DrawerProcessor . DrawerProcessor :





  • ,





  • ,





  • .





DrawerProcessor:





var drawer = new DrawerProcessor();
drawer.Draw(
    (row, blockIndex) => {
        //       

        if (row == 3)
            return new DrawerBlock { 
                Command = DrawerCommand.End 
            };

        if(blockIndex == 3)
            return new DrawerBlock {
                Value = Environment.NewLine,
                Command = DrawerCommand.NewLine
            };

        return new DrawerBlock {
            Value = $"[{row},{blockIndex}]",
            Command = DrawerCommand.Continue
        };
    },
    (blockStr, isEnd) => Console.Write(blockStr)
);


[0,0][0,1][0,2]
[1,0][1,1][1,2]
[2,0][2,1][2,2]

//  1.  DrawerProcessor.
//        -  
//     (   (row, blockIndex) => { .. }),
//   .
      
      



, , DrawerBlock.Displacement? - 1 , :





8         11
|---------|
      
      



“11” - . “11” - , “ “11”, “1” - “1” ”. ? “11” : DrawerBlock.Displacement = 2.





: “11” , (, -). ( “1” - “1”), .. " ". .





:





static class Block {
    public static DrawerBlock Continue(string val, int displacement = 1)         
        => new() {
            Command = DrawerCommand.Continue,
            Value = val,
            Displacement = displacement
        };

    public static DrawerBlock End() =>
        new() { Command = DrawerCommand.End };

    public static DrawerBlock NewLine() =>
        new() { 
            Command = DrawerCommand.NewLine, 
            Value = Environment.NewLine 
        };
}
      
      



:





return new DrawerBlock {
    Value = Environment.NewLine,
    Command = DrawerCommand.NewLine
};
      
      



:





return Block.NewLine();
      
      



1 ( (row, blockIndex) => { .. }). , . , / -  (row, blockIndex) => { .. } .





: 1 :





[0,0][0,1][0,2]
[1,0][1,1][1,2]
[2,0][2,1][2,2]
      
      



:





[   ][0,1][   ]
[1,0][   ][1,2]
[   ][2,1][   ]
      
      



(row, blockIndex) => { .. }.





: “ /” - , .. “ ” - . “ ” - .





.. , (. ), . .





. (“ ”, “ ”, “ ”, “ ”) ().





//  ""
DrawerBlock end(int row, int blockIndex) =>
    row == 3 ? Block.End() : null;

//  " "
DrawerBlock newLine(int row, int blockIndex) =>
    blockIndex == 3 ? Block.NewLine() : null;

//  "  "
DrawerBlock brick(int row, int blockIndex) => 
    Block.Continue($"[{row},{blockIndex}]");
      
      



:





public class BlockDrawer {
    readonly DrawerProcessor _DrawerProcessor;
    public BlockDrawer(DrawerProcessor drawerProcessor) {
        _DrawerProcessor = drawerProcessor 
            ?? throw new ArgumentNullException(nameof(drawerProcessor));
    }

    public void Draw(
        IReadOnlyCollection<Func<int, int, DrawerBlock>> blockDrawers, 
        Action<string, bool> onBlockDraw) {

        _DrawerProcessor.Draw(
                (row, blockIndex) => {
                    foreach (var bd in blockDrawers) {
                        var block = bd(row, blockIndex);
                        if (block != null)
                            return block;
                    }
                    return 
                        new DrawerBlock { Command = DrawerCommand.End };
                },
                onBlockDraw
            );
    }
}
      
      



:





//  :   
var blockDrawers = new Func<int, int, DrawerBlock>[] { 
    end,
    newLine,
    brick
};

var drawer = new DrawerProcessor();
var blockDrawer = new BlockDrawer(drawer);

blockDrawer.Draw(
    blockDrawers,
    (blockStr, isEnd) => Console.Write(blockStr));
      
      



, 1 - .





:





static void Main(string[] args) {

    DrawerBlock end(int row, int blockIndex) => ...;
    DrawerBlock newLine(int row, int blockIndex) => ...;
    DrawerBlock brick(int row, int blockIndex) => ...;

    //   
    DrawerBlock brickEmpty(int row, int blockIndex) =>
        ((row + blockIndex) % 2 == 0) ? Block.Continue($"[   ]") : null;

    var blockDrawers = new Func<int, int, DrawerBlock>[] { 
        end,
        newLine,
        brickEmpty, //    brick
        brick
    };

    var drawer = new DrawerProcessor();
    var blockDrawer = new BlockDrawer(drawer);

    blockDrawer.Draw(
        blockDrawers,
        (blockStr, isEnd) => Console.Write(blockStr));
}


[   ][0,1][   ]
[1,0][   ][1,2]
[   ][2,1][   ]
  
//  2.  DrawerProcessor.
//       .
//    main -   ,
//         .
      
      



: “ ”. . . , . , - .





2 Main. Main. . .





public interface IContextDrawerBlock<TDrawerContext> {
    int Priority { get; }
    DrawerBlock Draw(int row, int blockIndex, TDrawerContext context);
}
      
      



context, . , - context.RowCount:





class EndDrawer : IContextDrawerBlock<SampleDrawContext> {
    public int Priority => 10;
    public DrawerBlock Draw(int row, int blockIndex,
        SampleDrawContext context) 
      
        => row == context.RowCount ? Block.End() : null;
}
      
      



, :





public class ContextBlockDrawer<TDrawerContext> {
    readonly IReadOnlyCollection<IContextDrawerBlock<TDrawerContext>> _BlockDrawers;
    readonly BlockDrawer _Drawer;

    public ContextBlockDrawer(
        BlockDrawer drawer, 
        IReadOnlyCollection<IContextDrawerBlock<TDrawerContext>> blockDrawers) {

        _Drawer = drawer ?? throw ...

        _BlockDrawers = blockDrawers?.Any() == true
            ? blockDrawers.OrderBy(bd => bd.Priority).ToArray()
            : throw ...
    }

    public void Draw(TDrawerContext drawerContext,
        Action<string, bool> onBlockDraw) {

        var drawers = _BlockDrawers.Select(bd => {
            DrawerBlock draw(int row, int blockIndex) => 
                bd.Draw(row, blockIndex, drawerContext);

            return (Func<int, int, DrawerBlock>)draw;
        })
        .ToArray();

        _Drawer.Draw(drawers, onBlockDraw);
    }
}
      
      



:





//  ContextBlockDrawer

var drawer = new DrawerProcessor();
var blockDrawer = new BlockDrawer(drawer);


var blockDrawers = new IContextDrawerBlock<SampleDrawContext>[] {
    new EndDrawer(),
    new EndLineDrawer(),
    new BrickEmptyDrawer(),
    new BrickDrawer(),
};

var ctxBlockDrawer = new ContextBlockDrawer<SampleDrawContext>(
    blockDrawer, 
    blockDrawers);


//  ContextBlockDrawer

ctxBlockDrawer.Draw(
    new SampleDrawContext {
        RowCount = 3,
        BlockCount = 3
    },
    (blockStr, isEnd) => Console.Write(blockStr));

//  3.  ContextBlockDrawer.
//       .
//   .
//       ,
//      -    Priority.
      
      



: Priority, Priority . “ ”. .. ( ).





,

ContextBlockDrawer 3. ContextBlockDrawer () BlockDrawer . BlockDrawer, , () DrawerProcessor, .





:





ContextBlockDrawer -> ( )-> BlockDrawer -> -> DrawerProcessor.





“”.





3 ContextBlockDrawer “ ”. ( ) : “ ”:





- , ( - )





- , .





: , , “ ”:





//     
// (   )
var ctxBlockDrawer = new ContextBlockDrawer();
ctxBlockDrawer.BlockDrawer = blockDrawer;


//     
// (   )
public class ContextBlockDrawer<TDrawerContext> {
    ...
    public void Draw(TDrawerContext drawerContext, 
        Action<string, bool> onBlockDraw) {            
        ...
        var blockDrawer = ServiceLocator.Get<BlockDrawer>();
        ...
    }
}

//      
      
      



BlockDrawer (), ContextBlockDrawer BlockDrawer ( ):





public class ContextBlockDrawer<TDrawerContext> {
    readonly IReadOnlyCollection<IContextDrawerBlock<TDrawerContext>>  _BlockDrawers;
    readonly BlockDrawer _Drawer;

    public ContextBlockDrawer(
        IReadOnlyCollection<IContextDrawerBlock<TDrawerContext>> blockDrawers) {

        // 
        var drawer = new DrawerProcessor();
        _Drawer = new BlockDrawer(drawer);
        ...
    }
      
      



ContextBlockDrawer BlockDrawer : ContextBlockDrawer BlockDrawer, BlockDrawer. BlockDrawer( DrawerProcessor). .. , .





ASCII , - .





y x ContextBlockDrawer , :





public interface IContextDrawerBlock<TDrawerContext> {
    int Priority { get; }
    DrawerBlock Draw(int row, int blockIndex, TDrawerContext context);
}
      
      



Draw row blockIndex. . y :





public interface IartesianDrawerBlock<TDrawerContext> {
    int Priority { get; }
    DrawerBlock Draw(float x, float y, TDrawerContext context);
}
      
      



, :





class LineDrawer : IartesianDrawerBlock<artesianDrawerContext> {
    public int Priority => 40;

    public DrawerBlock Draw(float x, float y,
        artesianDrawerContext context) {

        var y1 = x; //   y=x
        
        //   y1    y
        // (c  )
        if (Math.Abs(y1 -y) <= context.Rounding)
            return Block.Continue("#");
            
        return null;
    }
}
      
      



IartesianDrawerBlock ContextBlockDrawer. , “Draw(int row, int blockIndex, TDrawerContext context)” “DrawerBlock Draw(float x, float y, TDrawerContext context)”:





public class artesianDrawerAdapter<TDrawerContext> : 
    IContextDrawerBlock<TDrawerContext>
    where TDrawerContext : IartesianDrawerAdapterContext {

    readonly IartesianDrawerBlock<TDrawerContext> _cartesianDrawer;
    public artesianDrawerAdapter(
    		IartesianDrawerBlock<TDrawerContext> cartesianDrawer) {

        _cartesianDrawer = cartesianDrawer ?? throw ...
    }
    
    public int Priority => _cartesianDrawer.Priority;

    public DrawerBlock Draw(int row, int blockIndex, TDrawerContext context) {

        float x = blockIndex / context.Scale + context.XMin;
        float y = context.YMax - row / context.Scale;
        return _cartesianDrawer.Draw(x, y, context);
    }
}

public interface IartesianDrawerAdapterContext {
    public float Scale { get; }
    public float XMin { get; }
    public float YMax { get; }
}
      
      



artesianDrawerAdapter - :





//  ctxBlockDrawer    

var drawer = new DrawerProcessor();
var blockDrawer = new BlockDrawer(drawer);


var blockDrawers = new IartesianDrawerBlock<artesianDrawerContext>[] {
        new EndDrawer(),
        new EndLineDrawer(),
        new LineDrawer(),
        new EmptyDrawer()
    }
    .Select(dd => 
        //  
        new artesianDrawerAdapter<artesianDrawerContext>(dd))
    .ToArray();

var ctxBlockDrawer = new ContextBlockDrawer<artesianDrawerContext>(
    blockDrawer,
    blockDrawers);


//  ctxBlockDrawer

ctxBlockDrawer.Draw(new artesianDrawerContext {
    XMin = -2,
    XMax = 30,
    YMin = -2,
    YMax = 8,
    Scale = 5,
    Rounding = 0.1F
},
(blockStr, isEnd) => Console.Write(blockStr));
      
      



: IContextDrawerBlock IartesianDrawerBlock “” - artesianDrawerAdapter.





:





// 

...
var ctxBlockDrawer = ...

var asciiDrawer = 
    new AsciiDrawer<artesianDrawerContext>(ctxBlockDrawer);


// 

asciiDrawer
    //    
    .OnBlockDraw((blockStr, isEnd) => Console.Write(blockStr))
    .Draw(new artesianDrawerContext {
        XMin = -2,
        XMax = 30,
        ...
    });

//  4.    AsciiDrawer.
      
      



AsciiDrawer:





public class AsciiDrawer<TDrawerContext> {
    readonly ContextBlockDrawer<TDrawerContext> _ContextBlockDrawer;
    readonly Action<string, bool> _onBlockDraw;
    public AsciiDrawer(
        ContextBlockDrawer<TDrawerContext> contextBlockDrawer, 
        Action<string, bool> onBlockDraw = null) {

        _ContextBlockDrawer = contextBlockDrawer ?? throw ...
        _onBlockDraw = onBlockDraw;
    }

    public AsciiDrawer<TDrawerContext> OnBlockDraw(
        Action<string, bool> onBlockDraw) {

        //   
        //  this (return this)   
        return new AsciiDrawer<TDrawerContext>(
            _ContextBlockDrawer, 
            onBlockDraw);
    }

    public void Draw(TDrawerContext context) {
        if (_onBlockDraw == null)
                throw new InvalidOperationException("Use .OnBlockDraw to set up draw output");

        _ContextBlockDrawer.Draw(context, _onBlockDraw);
    }
}
      
      



: AsciiDrawer “” - , . OnBlockDraw ( this). “” .





SingleInstance

4 , “”, IoC . AsciiDrawer.





Les objets ASCII du peintre ne stockent pas d'Ă©tats, ils sont Ă©galement «immuables», ce qui signifie que vous pouvez utiliser en toute sĂ©curitĂ© les mĂȘmes instances Ă  diffĂ©rents endroits. L'inclusion de nos objets peut ĂȘtre enregistrĂ©e dans le conteneur IoC en tant que SingleInstance.





En conséquence, dans la démo de Blazor WebAssembly , cliquez sur le bouton Exécuter, le code suivant:





var res = new StringBuilder();

AsciiDrw
    .OnBlockDraw((blockStr, isEnd) => {
        res.Append(blockStr);
        if (isEnd)
            //  UI
            Res = res.ToString();
    })
    .Draw(new FuncsDrawerContext {

        //  
        Rounding = Rounding,
        Scale = Scale,
        XMin = Xmin,
        XMax = Xmax,
        YMin = Ymin,
        YMax = Ymax,

        //   y  x
        Functions = funcs
    });
      
      



La démo utilise les blocs suivants:





new EndDrawer(),
new EndLineDrawer(),
new FuncsDrawer(), //     
new XAxisDrawer(), //   X
new YAxisDrawer(), //   Y
new EmptyDrawer()
      
      



Vous pouvez Ă©galement penser Ă :





  • un bloc qui peint la zone sous le graphique,





  • un bloc qui affiche une Ă©chelle sur les axes,





  • bloc qui signe les points d'intersection des graphiques.





Conclusion

Comme vous pouvez le voir, mĂȘme un problĂšme scolaire peut ĂȘtre sĂ©rieusement confondu - l'essentiel est de connaĂźtre les principes et les schĂ©mas.








All Articles