"Conectividade" do código usando o exemplo de um gerador de plotagem ASCII, um utilitário para operações com intervalos e demonstração no Blazor WebAssembly

Trabalhar com períodos pode ser confuso. Imagine que você tem um aplicativo de contabilidade. E você precisa obter os períodos em que o funcionário trabalhou no esquema “2 a 2” antes da indexação do salário. Neste caso, é necessário levar em consideração férias, mudanças de horários de trabalho, dispensas / reintegrações, transferências para outros departamentos e outras atividades de pessoal. Essas informações são armazenadas na forma de pedidos, que possuem uma “Data de início” e uma “Data de término”, ou seja. você tem períodos de tempo para operar.





Por exemplo, encontre a interseção de todos os intervalos:





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

//       
      
      



O utilitário anunciado se destina a resolver esses problemas.





Trabalhar com períodos na representação visual é muito mais fácil, então para teste (também existe um conjunto de testes) e documentação, fiz uma geração simples de imagens ASCII, conforme mostrado acima.





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





Os objetos ASCII do pintor não armazenam estados, eles também são “imutáveis”, o que significa que você pode usar com segurança as mesmas instâncias em diferentes lugares. Incluindo nossos objetos podem ser registrados no container IoC como SingleInstance.





Como resultado, na demonstração do Blazor WebAssembly ao clicar no botão Executar, o seguinte código:





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



A demonstração usa os seguintes blocos:





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



Você também pode pensar em:





  • um bloco que pinta a área sob o gráfico,





  • um bloco que exibe uma escala nos eixos,





  • bloco que sinaliza os pontos de intersecção dos gráficos.





ConclusĂŁo

Como você pode ver, até mesmo um problema escolar pode ser seriamente confundido - o principal é conhecer os princípios e padrões.








All Articles