مقال آخر عن البرمجة التفاعلية. وفقط لا تدور عينيك على هذا الخط وتتحدث بصوت عالٍ بصوت ضعيف - "حسنًا ، ماذا يمكنك أن تخبرني أيضًا عن البرمجة التفاعلية ... إيه؟" إنها مختلفة قليلاً عن مجموعة من الآخرين ، مكتوبة مثل ورق الكربون ، لذلك قد تبدو بعض الأشياء فيه ... غريبة أو غير مناسبة تمامًا ، مثل الفكاهة المصنفة.
لا يهم على الإطلاق ما إذا كنت تعرف البيان التفاعلي عن ظهر قلب ، وما إذا كان هناك ضغط في قهوة الصباح ، أو إذا لمست جميع أنواع الناشرين والمشتركين مع هذه الأقلام ، أو كتبت رمزًا قديمًا متزامنًا جيدًا للحجب. أو ربما مؤخرًا فقط ، قام شخص ما بتقريره الإعلاني الصريح عن المستقبل المشرق والنشوة الجنسية (حسناً ، أو النافثة للحبر ، وهنا التفاصيل الدقيقة للترجمة تقرر كل شيء) ، بالطبع ، من استخدام إحدى المكتبات التفاعلية ، أثار اهتمامًا بالتكنولوجيا الجديدة في عينيك.
سيكون من المثير للاهتمام.
الارتفاع إلى المطلق
وهكذا ، دعونا نتخيل أننا مجنونون تمامًا ... على الرغم من أنه سيتم تقديم الكلمة هنا لمزيد من الدقة السياسية ، لأن كل شخص يعرف المؤلف يدرك أنه يجلس طوال اليوم في شقة مغلقة. إنه يعمل ليلًا ، ويدخن ثلاثة نرجيلات يوميًا ، ويخرج للخارج عدة مرات في الأسبوع. يحدث هذا عادة لمقابلة المعالج ، ولكن في بعض الأحيان يكون السبب مختلفًا تمامًا - ايكيا.
وهكذا ، نأخذ الرمز القديم المتزامن ، لكن:
int result = 1 + 2 * 4;
ونحن نحاول إعادة تفعيل هذا الأمر ، لأنه يبدو لنا ... على الرغم من أنه لا ، إنه كثير للغاية ، لن يقوم أحد بذلك في أذهانهم الصحيحة. لا احد في عقلك الصحيح.
Mono<Integer> result = Flux.concat( Mono.just(1), Flux.concat(Mono.just(2), Mono.just(4)) .reduce((a, b) -> a * b)) .reduce((a, b) -> a + b); StepVerifier.create(result) .expectNext(9) .expectComplete() .verify();
ولكن كما لاحظتم ، لقد فعلت ذلك. و يرجى ملاحظة! لقد حرصت نفسي على استخدام اثنين من الخدمات المصغرة في هذا المثال ، أحدهما للإضافة والآخر للضرب ، والذي كان يجب علي التواصل عبر rSocket
و kafka
.
بالمناسبة ، كان الاحماء. قبل أن نبدأ ، أود أن أقدم نصيحتين. وهكذا ، النصيحة الأولى: كن مستعدًا دائمًا لحقيقة أن زملائك يمكن أن يكونوا مرضى عقلانيين مريضين حقًا مع مجموعة من مجمعات الأطفال التي ستظهر في شكل رمز ، وليس من أفضل الحلول ، والحلول المعمارية الأنيقة للغاية.
نصيحة اثنين. قررت أن أتركها في نهاية المقال. لكن إذا قررت بعد كل شيء تراه ، عليك الابتعاد عن هذا d٪ & @ a ، فأنت تعرف أين تبحث عنه.
مثال حقيقي أكثر
لنأخذ مثالًا أكثر واقعية قليلاً. على سبيل المثال ... الأشجار. لماذا بالضبط لهم؟ لأننا جميعًا نحب الأشجار ككائن في الحياة الحقيقية وبنية بيانات. هذا هو واحد من أهم أجزاء العالم من حولنا. الأشجار تنتج الأكسجين حتى نتمكن من التنفس.
تعيش العديد من الحيوانات على الأشجار ، أو في أماكن تراكم هذه الكائنات (وتسمى هذه الأماكن حديقة أو غابة ، اعتمادًا على) ، ولكن الحيوانات ، حتى الأشخاص الذين لا مأوى لهم ، تعيش في صناديق من الورق المقوى كانت الأشجار في الأصل.
بالمناسبة ، حقيقة مثيرة للاهتمام ، منذ العصور القديمة ، كانت البشرية تستخرج من الأشجار واحدة من أكثر المواد المستخدمة في الحياة البشرية - الخشب. تخيل فقط ، كل تلك العصي التي نضيفها في عجلاتنا طوال حياتنا ، وفي الوقت نفسه نلوم الجميع ، مصنوعين من الخشب أيضًا.
لدينا شجرة ، شجرة عادية وغير ملحوظة نمت على العشب من الكود وأحلمنا ذات يوم عندما تكبر ، تتحول إلى BST:
public class TreeNode { public int val; public TreeNode left, right; public TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; } public TreeNode(int val) { this(val, null, null); }
وعند نقطة واحدة ، على سبيل المثال ، يوم الاثنين في الساعة السادسة صباحًا ، بعد ليلة بلا نوم ، كان لدى أحد المبرمجين فكرة جعله تفاعليًا. وقد فعل ذلك. لأنه رجل عمل.
public class TreeNode { public final Mono<Integer> value; public final Mono<TreeNode> left; public final Mono<TreeNode> right; public TreeNode(Mono<Integer> value, Mono<TreeNode> left, Mono<TreeNode> right) { this.value = value; this.left = left; this.right = right; } public TreeNode(int value, TreeNode left, TreeNode right) { this(Mono.just(value), Mono.justOrEmpty(left), Mono.justOrEmpty(right)); } public TreeNode(int value) { this(Mono.just(value), Mono.empty(), Mono.empty()); } public Flux<TreeNode> flux() { return Flux.concat( left.flatMapMany(TreeNode::flux), Mono.just(this), right.flatMapMany(TreeNode::flux) ); } @Override public String toString() { return flux() .flatMap(n -> n.value) .collectList() .map(Object::toString) .block(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TreeNode treeNode = (TreeNode) o; return Objects.equals(value.block(), treeNode.value.block()); } @Override public int hashCode() { return value.block(); }
تشعر أن السؤال معلق في الهواء - "لماذا؟". أولاً ، إنه جميل ، وثانيًا ، إنه قابل للتوسع جدًا ، نظرًا لأنه غير متزامن ، كما أنه صديق للبيئة ولن تأتي جريتا تونبرج إلينا مرة واحدة في قطار به حزام ضخم ... أنت تعرف ماذا أقصد. و لا تنسى الآن أننا نواجه ضغوطات. وبالمناسبة ، أذكرك بأنه لا يمكن تنفيذ إعادة بيع بدون تغطية اختبار.
public class TreeNodeTest { @Test public void testFlux() { TreeNode tree = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(3)), new TreeNode(7, new TreeNode(6), new TreeNode(9))); StepVerifier.create(tree.flux().flatMap(n -> n.value)) .expectNext(1, 2, 3, 4, 6, 7, 9) .expectComplete() .verify(); } }
ممتاز. لقد تم البدء ، والآن يتعين علينا إعادة sumOfLeftLeaves
ثلاث طرق بسيطة هي invert
، sumOfLeftLeaves
و searchBST
في searchBST
القديمة والمملة والمتزامنة وإضافة بعض الاختبارات.
قلب
وهكذا ، لدينا بالفعل طريقة invert
، لكن لسوء الحظ ، ليست تفاعلية ، انظر إلى أي مدى تبدو حزينة ، والإيجابيات الوحيدة لهذه الطريقة هي أنه من السهل قراءتها ومن السهل أن نفهم أننا إذا قلبنا الشجرة ، فعندئذ نذهب بشكل متكرر من خلال جميع العقد ، وإنشاء نسخ ومبادلة أطفالها. وهذا هو ، في الطفل الأيسر من العقدة الجديدة ، وضعنا العقدة المقلوبة من الطفل الأيمن والعكس صحيح. وفي كل مكان هذه الشيكات لاغية.
public TreeNode invert() { return invert(this); } private TreeNode invert(TreeNode root) { if (root == null) return null; TreeNode swap = new TreeNode(root.val); swap.right = invert(root.left); swap.left = invert(root.right); return swap; }
انظروا إلى ما حدث عندما أعادنا كتابته ، ولعب بألوان جديدة ، وكمكافأة ، حصلنا على سلامة خالية مجانًا.
public Mono<TreeNode> invert() { return Mono.just(this) .map(n -> new TreeNode(n.value, n.right.flatMap(TreeNode::invert), n.left.flatMap(TreeNode::invert) )); } @Test public void testInvert() { TreeNode tree = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(3)), new TreeNode(7, new TreeNode(6), new TreeNode(9))); Flux<Integer> inverted = tree.invert() .flatMapMany(TreeNode::flux) .flatMap(n -> n.value); StepVerifier.create(inverted) .expectNext(9, 7, 6, 4, 3, 2, 1) .expectComplete() .verify(); }
sumOfLeftLeaves
عادة ما يتم تقسيم الناس إلى نوعين ، النوع الأول هو أولئك الذين يفهمون ما ستفعله هذه الطريقة من اسمه ، وجميع الأشخاص الآخرين الذين لم يتركوا غرفتهم لسنوات ولم يروا الأشجار.
أستطيع أن أساعد النوع الثاني من الناس بنصيحة من أخصائي النفسي ، الذي من المحتمل أن يكون قادرًا على وضع نظام علاج. ونقول معنى هذه الطريقة. تُعتبر العقدة leaf
إذا لم يكن لها أطفال ، وتركت إذا نمت عند الأصل من المؤشر left
. هذا مكتوب باسم الطريقة.
ولكن لماذا الكلمات ، دعونا ننكب على العمل ، ونرى كيف كان ...
public int sumOfLeftLeaves() { return sumOfLeftLeaves(false, this); } public int sumOfLeftLeaves(boolean left, TreeNode root) { if (root == null) return 0; if (root.left == null && root.right == null && left) return root.val; return sumOfLeftLeaves(true, root.left) + sumOfLeftLeaves(false, root.right); }
... وكيف أصبح
public Mono<Integer> sumOfLeftLeaves() { return sumOfLeftLeaves(Mono.just(this), false) .flatMap(n -> n.value) .reduce(Integer::sum); } private Flux<TreeNode> sumOfLeftLeaves(Mono<TreeNode> node, boolean left) { return node .flux() .concatMap(n -> Flux.concat( sumOfLeftLeaves(n.left, true), Flux.first(n.left, n.right) .map(x -> new TreeNode(0)) .switchIfEmpty( Mono.just(n) ) .filter(x -> left), sumOfLeftLeaves(n.right, false) )); } @Test public void testSumOfLeftLeaves() { TreeNode tree = new TreeNode(3, new TreeNode(9, new TreeNode(11), null), new TreeNode(20, new TreeNode(15), new TreeNode(7)) ); StepVerifier.create(tree.sumOfLeftLeaves()) .expectNext(26) .expectComplete() .verify(); }
نجاح باهر! رد الفعل جدا. متزامن كثيرا. رائع جدا. حتى لاغية السلامة. الكثير من الضغط. قابلة للتطوير ...
بالمناسبة ، لا يزال لدينا طريقة أخرى ، حيث أعدك ، سنوجه كل هذه القوة إلى الاتجاه الصحيح.
searchBST
تحلم كل شجرة بأن تصبح شجرة بحث ثنائية ، وهذه الطريقة ستساعدنا في البحث عنها. هذا يقول كل شيء ، لذلك فقد أوضحت على الفور كم كان فظيعًا ، وكيف أصبح رائعًا.
public TreeNode searchBST(int val) { return searchBST(this, val); } public TreeNode searchBST(TreeNode root, int val) { if (root == null) return null; if (val < root.val) return searchBST(root.left, val); else if (val > root.val) return searchBST(root.right, val); else return root; }
فقط أشعر به:
public Mono<TreeNode> testSearchBST(int val) { return searchBST(Mono.just(this), val); } private Mono<TreeNode> searchBST(Mono<TreeNode> root, int val) { return root.flatMap(node -> node.value .filter(v -> v > val) .flatMap(v -> searchBST(node.left, val)) .switchIfEmpty(node.value .filter(v -> v < val) .flatMap(v -> searchBST(node.right, val)) .switchIfEmpty(node.value .filter(v -> v == val) .flatMap(v -> root) ) )); } @Test public void searchBST() { TreeNode tree = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(3)), new TreeNode(7)); StepVerifier.create(tree.searchBST(3).flatMap(n -> n.value)) .expectNext(3) .expectComplete() .verify(); }
لا شيء أكثر لإضافته. هذا هو البرمجة التفاعلية ، هو الأكثر.
لم تنته بعد
عندما تفعل أشياء رائعة حقًا ، فمن الصعب جدًا التوقف عنها. أنت متعب ، وعيناك متقاربتان ، وتريد أن تنام ، لكن في داخلكما تشعران بدفق كبير من الدافع. أنت تفعل كل شيء بشكل صحيح ، لا يمكنك التوقف ، والتقاط هذا الشعور ، والاستمرار في جعل العالم مكانًا أفضل.
لذلك ، بعد أن تخلصت من الأشجار ، أنت ، دون تردد ، انتقل إلى القوائم القديمة الجيدة والمتصلة ببساطة. وقتهم لم ينته بعد ، لا يزال الآلاف من الأشخاص في المقابلات الفنية يقومون بفحصهم للتأكد من صحتهم. حان الوقت للقيام بذلك بشكل تفاعلي.
public class ListNode { public int val; public ListNode next; public ListNode(int val) { this.val = val; } public ListNode(int val, ListNode next) { this.val = val; this.next = next; } public static ListNode of(int... array) { if (array.length < 1) return null; ListNode head = new ListNode(array[0]); ListNode tail = head; for (int i = 1; i < array.length; i++) { ListNode next = new ListNode(array[i]); tail.next = next; tail = next; } return head; } public ListNode last() { if (next != null) return next.last(); return this; } } public class ListTestNode { public Mono<Boolean> hasCycle(ListNode head) { return Mono.justOrEmpty(head) .flatMapMany(node -> { Flux<ListNode> flux = Flux.generate(() -> head, (n, sink) -> { if (n == null) { sink.complete(); return null; } sink.next(n); return n.next; }); Flux<ListNode> fast = flux.skip(1); Flux<ListNode> slow = flux.flatMap(n -> Flux.just(n, n)); return fast.zipWith(slow); }) .any(objects -> objects.getT1() == objects.getT2()) .defaultIfEmpty(false); } @Test public void hasCycle() { StepVerifier.create(hasCycle(null)) .expectNext(false) .expectComplete() .verify(); ListNode withoutCycle = ListNode.of(1, 2, 3, 4, 5, 6); StepVerifier.create(hasCycle(withoutCycle)) .expectNext(false) .expectComplete() .verify(); ListNode withCycle = ListNode.of(1, 2, 3, 4, 5, 6); withCycle.last().next = withCycle.next.next; StepVerifier.create(hasCycle(withCycle)) .expectNext(true) .expectComplete() .verify(); }
بما أننا تطرقنا إلى موضوع المقابلات الفنية ، فلا ينبغي أن ننسى المهمة المحببة المتمثلة في التحقق من العلبة بين قوسين. يجب أن يستمر العرض!
public Mono<Boolean> isValidParentheses(String s) { return Flux.range(0, s.length()) .map(s::charAt) .reduceWith(() -> "", (str, c) -> { if (c == '{' || c == '[' || c == '(') return str + c; char last = str.charAt(str.length() - 1); if (c == '}' && last != '{') return str; if (c == ']' && last != '[') return str; if (c == ')' && last != '(') return str; return str.substring(0, str.length() - 1); }) .map(String::isEmpty); } @Test public void testIsValidParentheses() { StepVerifier.create(isValidParentheses("()")) .expectNext(true) .expectComplete() .verify(); StepVerifier.create(isValidParentheses("()[]{}")) .expectNext(true) .expectComplete() .verify(); StepVerifier.create(isValidParentheses("{()[]()}")) .expectNext(true) .expectComplete() .verify(); StepVerifier.create(isValidParentheses("()")) .expectNext(true) .expectComplete() .verify(); StepVerifier.create(isValidParentheses("(]")) .expectNext(false) .expectComplete() .verify(); StepVerifier.create(isValidParentheses("([)]")) .expectNext(false) .expectComplete() .verify(); }
نجاح باهر. غير قابل للتغيير ... رد فعل كثير.
نصيحة رقم اثنين
بالمناسبة ، كما وعدت ، في نهاية المقال هو النصيحة رقم اثنين: .
في بعض الأحيان سوف تبدو سخيفة وغير عملي.