Das Voranschreiten eines Algorithmus' zu visualisieren war bislang eigentlich unmöglich, denn der Computer kann ja nicht innehalten. Natürlich kann man ihn mit
Man hat die Algos immer zur völligen Unkenntlichkeit umstrukturieren müssen, um bestimmte Zustände, die ganz normal im Algo auftreten, als konsistente Objekte verfügbar zu bekommen, die man einerseits im Gui präsentieren kann, von denen man andererseits aber auch den nächsten Schritt des Algos gehen kann. Eine einfache
Aber dank Async kann der Computer nun innehalten, und diese entstellten Mutanten dürfen aussterben - mögen sie ruhen in Frieden
Das einfachste Innehalten geht mit
Das zweit-einfachste ist, auf ein AutoResetEvent zu warten, welches der User per Button-Click durchschaltet:
Die Wait-Methode (#3) ist ebenso aufzurufen wie Task.Delay, und sie schiebt ebenso einen Delay ein, nur jetzt bestimmt der User, wanns weiter geht.
Als praktisches Demo habe ich den BubbleSort-Algo hergenommen - hier the whole Story:
Eine
Es gibt die 3 Buttons
Alles selbsterklärend, wie man sieht: Nomen est Omen!
Innegehalten wird in Zeilen #32, sowie (#46), #47
Einzig zu erklären vlt. das
Auf das erstaunlichste, nämlich den Async/Await-Pattern selbst gehe ich hier nicht ein, weil darauf bin ich hier schon eingehender drauf eingegangen: Async, Await und Task
Naja - kurz: Am Await-Aufruf bricht eine Methode verfrüht ab, und der Aufrufer fährt fort, ohne blockiert worden zu sein.
Währenddessen wird das Awaitete in einem Task abgearbeitet. Ist der Task fertig hopft die Methode auf einmal wieder rein, und setzt fort als sei nichts gewesen.
Klar soweit ?
same in c#
Vielen Dank übrigens an @RodFromGermany, @NyuQz und @RushDen, weil die haben mich inspiriert, wie man so sagt, hier und hier
Thread.Sleep()
anhalten, aber das hält auch die Oberfläche an - also visualisiert wird dabei auch nix Man hat die Algos immer zur völligen Unkenntlichkeit umstrukturieren müssen, um bestimmte Zustände, die ganz normal im Algo auftreten, als konsistente Objekte verfügbar zu bekommen, die man einerseits im Gui präsentieren kann, von denen man andererseits aber auch den nächsten Schritt des Algos gehen kann. Eine einfache
For
-Schleife mutiert mindestens zu einem Button, einer Eventhandler-Methode und einer Klassen-Variable - von der Schleife bleibt nichts .Aber dank Async kann der Computer nun innehalten, und diese entstellten Mutanten dürfen aussterben - mögen sie ruhen in Frieden
Das einfachste Innehalten geht mit
Await Task.Delay(500)
: ein Delay von 500ms einzuschieben.Das zweit-einfachste ist, auf ein AutoResetEvent zu warten, welches der User per Button-Click durchschaltet:
Als praktisches Demo habe ich den BubbleSort-Algo hergenommen - hier the whole Story:
VB.NET-Quellcode
- Imports System.ComponentModel
- Imports System.Threading
- Imports System.Runtime.CompilerServices
- Public Class frmSortVisualizerVb
- Private _List As New BindingList(Of Integer)()
- Private _Blocker As New AutoResetEvent(True)
- Public Sub New()
- InitializeComponent()
- ListBox1.DataSource = _List
- End Sub
- Private Sub Button_Click(sender As Object, e As EventArgs) Handles btBubbleSort.Click, btReset.Click, btStep.Click
- Select Case True
- Case sender Is btReset : Generate(30, 30)
- Case sender Is btBubbleSort : BubbleSort(_List)
- Case sender Is btStep : _Blocker.Set()
- End Select
- End Sub
- Private Sub ckStepwise_CheckedChanged(sender As Object, e As EventArgs) Handles ckStepwise.CheckedChanged
- btStep.Enabled = ckStepwise.Checked
- _Blocker.[Set]()
- End Sub
- Private Async Sub Generate(max As Integer, count As Integer)
- _List.Clear()
- Dim rnd = New Random(0)
- For i = 0 To count - 1
- If ckStepwise.Checked Then Await Wait()
- _List.Add(rnd.[Next](max))
- Next
- End Sub
- Private Async Sub BubbleSort(lst As IList(Of Integer))
- 'repeat: run up, compare each element with its successor - if greater: Swap
- 'topSwap/lastSwap consider, that the elements behind the last swap-Position of the previous Run-up already are sorted
- Dim topSwap = lst.Count - 2
- Dim lastSwap As Integer
- Do
- lastSwap = 0
- For i = 0 To topSwap
- If lst(i) > lst(i + 1) Then
- 'If ckStepwise.Checked Then Await Task.Delay(500)
- If ckStepwise.Checked Then Await Wait()
- lst.Swap(i, i + 1)
- lastSwap = i
- End If
- Next
- topSwap = lastSwap - 1
- Loop Until lastSwap = 0
- End Sub
- Private Function Wait() As Task
- Return Task.Run(Function() _Blocker.WaitOne())
- End Function
- End Class
- Friend Module Extensions
- <Extension> _
- Public Sub Swap(Of T)(Subj As IList(Of T), I1 As Integer, I2 As Integer)
- Dim Tmp = Subj(I1)
- Subj(I1) = Subj(I2)
- Subj(I2) = Tmp
- End Sub
- End Module
Listbox
, gebunden an eine BindingList(Of Integer)
(#7, 12).Es gibt die 3 Buttons
btReset
, btBubbleSort
und btStep
, eine Checkbox ckStepwise
, sowie die beiden Algorithmen Generate
und BubbleSort
.Alles selbsterklärend, wie man sieht: Nomen est Omen!
Innegehalten wird in Zeilen #32, sowie (#46), #47
Einzig zu erklären vlt. das
AutoResetEvent
: Dessen Methode .WaitOne()
blockiert den aktuellen Thread so lange, bis von einem anderen Thread aus mit .Set()
freigeschaltet wird.Auf das erstaunlichste, nämlich den Async/Await-Pattern selbst gehe ich hier nicht ein, weil darauf bin ich hier schon eingehender drauf eingegangen: Async, Await und Task
Naja - kurz: Am Await-Aufruf bricht eine Methode verfrüht ab, und der Aufrufer fährt fort, ohne blockiert worden zu sein.
Währenddessen wird das Awaitete in einem Task abgearbeitet. Ist der Task fertig hopft die Methode auf einmal wieder rein, und setzt fort als sei nichts gewesen.
Klar soweit ?
C#-Quellcode
- public partial class frmSortVisualizerCs : Form {
- private BindingList<int> _List = new BindingList<int>();
- private AutoResetEvent _Blocker = new AutoResetEvent(true);
- public frmSortVisualizerCs() {
- InitializeComponent();
- listBox1.DataSource = _List;
- btStep.Click += (s, e) => _Blocker.Set();
- btReset.Click += (s, e) => Generate(30, 30);
- btBubbleSort.Click += (s, e) => BubbleSort(_List);
- ckStepwise.CheckedChanged += ckStepwise_CheckedChanged;
- }
- void ckStepwise_CheckedChanged(object sender, EventArgs e) {
- btStep.Enabled = ckStepwise.Checked;
- _Blocker.Set();
- }
- private async void Generate(int max, int count) {
- _List.Clear();
- var rnd = new Random(0);
- for (var i = -1; ++i < count; ) {
- if (ckStepwise.Checked) await Wait();
- _List.Add(rnd.Next(max));
- }
- }
- private async void BubbleSort(IList<int> lst) {
- //repeat: run up, compare each element with its successor - if greater: Swap
- //topSwap/lastSwap consider, that the elements behind the last swap-Position of the previous Run-up already are sorted
- var topSwap = lst.Count - 2;
- int lastSwap;
- do {
- lastSwap = 0;
- for (var i = 0; i <= topSwap; ++i) {
- if (lst[i] > lst[i + 1]) {
- //if (ckStepwise.Checked) await Task.Delay(500);
- if (ckStepwise.Checked) await Wait();
- lst.Swap(i, i + 1);
- lastSwap = i;
- }
- }
- topSwap = lastSwap-1;
- } while (lastSwap != 0);
- }
- private Task Wait() {
- return Task.Run(() => _Blocker.WaitOne());
- }
- }
- public static class Extensions {
- public static void Swap<T>(this IList<T> Subj, int I1, int I2) {
- var Tmp = Subj[I1];
- Subj[I1] = Subj[I2];
- Subj[I2] = Tmp;
- }
- }
Vielen Dank übrigens an @RodFromGermany, @NyuQz und @RushDen, weil die haben mich inspiriert, wie man so sagt, hier und hier
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()