Algebraic data types: Difference between revisions

Added a Java (JDK 21 + Preview features) translation for the existing Kotlin solution. Removed the omission tag for Java.
m (syntax highlighting fixup automation)
imported>Maruseron
(Added a Java (JDK 21 + Preview features) translation for the existing Kotlin solution. Removed the omission tag for Java.)
 
(4 intermediate revisions by 4 users not shown)
Line 14:
=={{header|Bracmat}}==
 
<syntaxhighlight lang="bracmat">( ( balance
= a x b y c zd
. !arg
Line 63:
 
Test:
<syntaxhighlight lang="bracmat">( ( it allows for terse code which is easy to read
, and can represent the algorithm directly
.
Line 74:
 
Output:
<syntaxhighlight lang="bracmat">(tree=
B
. ( B
Line 102:
C++ templates have a robust pattern matching facility, with some warts - for example, nested templates cannot be fully specialized, so we must use a dummy template parameter. This implementation uses C++17 deduced template parameters for genericity.
 
<syntaxhighlight lang="cpp">enum Color { R, B };
template<Color, class, auto, class> struct T;
struct E;
Line 151:
Although C++ has structured bindings and pattern matching through function overloading, it is not yet possible to use them together so we must match the structure of the tree being rebalanced separately from decomposing it into its elements. A further issue is that function overloads are not ordered, so to avoid ambiguity we must explicitly reject any (ill-formed) trees that would match more than one case during rebalance.
 
<syntaxhighlight lang="cpp">#include <memory>
#include <variant>
 
Line 258:
Translation of several
{{works with|C sharp|8}}
<syntaxhighlight lang="csharp">using System;
 
class Tree
Line 356:
{{libheader|toadstool}}
 
<syntaxhighlight lang="lisp">(mapc #'use-package '(#:toadstool #:toadstool-system))
(defstruct (red-black-tree (:constructor tree (color left val right)))
color left val right)
Line 441:
 
=={{header|EchoLisp}}==
<syntaxhighlight lang="scheme">
;; code adapted from Racket and Common Lisp
;; Illustrates matching on structures
Line 471:
</syntaxhighlight>
{{out}}
<syntaxhighlight lang="scheme">
(define (t-show n (depth 0))
(when (!eq? 'empty n)
Line 515:
{{trans|Erlang}}
But, it changed an API into the Elixir style.
<syntaxhighlight lang="elixir">defmodule RBtree do
def find(nil, _), do: :not_found
def find({ key, value, _, _, _ }, key), do: { :found, { key, value } }
Line 580:
The <code>pcase</code> macro was added in Emacs 24.1. It's auto-loaded, so there's no need to add <code>(require 'pcase)</code> to your code.
 
<syntaxhighlight lang="lisp">(defun rbt-balance (tree)
(pcase tree
(`(B (R (R ,a ,x ,b) ,y ,c) ,z ,d) `(R (B ,a ,x ,b) ,y (B ,c ,z ,d)))
Line 639:
 
The code used here is extracted from [https://gist.github.com/mjn/2648040 Mark Northcott's GitHubGist].
<syntaxhighlight lang="erlang">
-module(rbtree).
-export([insert/3, find/2]).
Line 714:
 
=={{header|F_Sharp|F#}}==
<syntaxhighlight lang="fsharp">
// Pattern Matching. Nigel Galloway: January 15th., 2021
type colour= |Red |Black
Line 734:
 
However, pattern matching on interfaces (via the type switch statement and type assertions) is limited to matching the implementing type and so the balance() method is not very pleasant.
<syntaxhighlight lang="go">package main
 
import "fmt"
Line 860:
=={{header|Haskell}}==
 
<syntaxhighlight lang="haskell">data Color = R | B
data Tree a = E | T Color (Tree a) a (Tree a)
 
Line 886:
The following code represents a best effort translation of the current Haskell implementation of this task:
 
<syntaxhighlight lang=J"j">insert=:{{
'R';'';y;a:
:
Line 928:
Example use:
 
<syntaxhighlight lang=J"j"> 3 insert 2 insert 5
┌─┬───────┬─┬───────┐
│R│┌─┬┬─┬┐│3│┌─┬┬─┬┐│
Line 937:
Note that by convention we treat the root node as black. This approach always labels it with 'R' which we ignore. However, if we wish to validate these trees, we must account for the discrepancy.
 
<syntaxhighlight lang=J"j">NB. always treat root of tree as black
validate=: {{
if. 0=#y do. 1 return. end.
Line 960:
For example:
 
<syntaxhighlight lang=J"j"> ?.~20
14 18 12 16 5 1 3 0 6 13 9 8 15 17 2 10 7 4 19 11
insert/?.~20
Line 978:
 
Finally a caution: red black trees exhibit poor cache coherency. In many (perhaps most or all) cases an amortized hierarchical linear sort mechanism will perform better than a red black tree implementation. (And that characteristic is especially true of this particular implementation.)
 
=={{header|Java}}==
{{trans|Kotlin}}
{{works with|OpenJDK|21 (Preview)}}
 
Java 21 has added support for ADTs (in the form of sealed types), which are narrowable through a switch expression. Despite having no fully-fledged pattern matching, a combination of record deconstruction patterns and guarded patterns allows for something very similar through switch expressions:
 
<syntaxhighlight lang="java">public class Task {
enum Color { R, B }
sealed interface Tree<A extends Comparable<A>> permits E, T {
default Tree<A> insert(A a) {
return switch(ins(a)) {
case T(_, var l, var v, var r) -> new T<>(Color.B, l, v, r);
case E() -> new E<>();
};
}
 
Tree<A> ins(A a);
}
 
record E<A extends Comparable<A>>() implements Tree<A> {
@Override
public Tree<A> ins(A a) {
return new T<>(Color.R, new E<>(), a, new E<>());
}
 
@Override
public String toString() { return "E"; }
}
 
record T<A extends Comparable<A>>(Color color, Tree<A> left,
A value, Tree<A> right) implements Tree<A> {
@Override
public Tree<A> ins(A a) {
return switch(Integer.valueOf(a.compareTo(value))) {
case Integer i when i < 0 -> new T<>(color, left.ins(a), value, right).balance();
case Integer i when i > 0 -> new T<>(color, left, value, right.ins(a)).balance();
default -> this;
};
}
 
private Tree<A> balance() {
if (color == Color.R) return this;
return switch (this) {
// unnamed patterns (case T<A>(_, ...)) are a JDK21 Preview feature
case T<A>(_, T<A>(_, T<A>(_, var a, var x, var b), var y, var c), var z, var d)
when left instanceof T<A> le && le.left instanceof T<A> le_le &&
le.color == Color.R && le_le.color == Color.R ->
new T<>(Color.R, new T<>(Color.B, a, x, b), y, new T<>(Color.B, c, z, d));
case T<A>(_, T<A>(_, var a, var x, T<A>(_, var b, var y, var c)), var z, var d)
when left instanceof T<A> le && le.right instanceof T<A> le_ri &&
le.color == Color.R && le_ri.color == Color.R ->
new T<>(Color.R, new T<>(Color.B, a, x, b), y, new T<>(Color.B, c, z, d));
case T<A>(_, var a, var x, T<A>(_, T<A>(_, var b, var y, var c), var z, var d))
when right instanceof T<A> ri && ri.left instanceof T<A> ri_le &&
ri.color == Color.R && ri_le.color == Color.R ->
new T<>(Color.R, new T<>(Color.B, a, x, b), y, new T<>(Color.B, c, z, d));
case T<A>(_, var a, var x, T<A>(_, var b, var y, T<A>(_, var c, var z, var d)))
when right instanceof T<A> ri && ri.right instanceof T<A> ri_ri &&
ri.color == Color.R && ri_ri.color == Color.R ->
new T<>(Color.R, new T<>(Color.B, a, x, b), y, new T<>(Color.B, c, z, d));
default -> this;
};
}
 
@Override
public String toString() {
return STR."T[\{color}, \{left}, \{value}, \{right}]"; // String templates are a JDK 21 Preview feature
}
}
 
public static void main(String[] args) {
Tree<Integer> tree = new E<>();
for (var i : IntStream.rangeClosed(1, 16).toArray()) {
tree = tree.insert(i);
}
System.out.println(tree);
}
}
</syntaxhighlight>
{{out}}
<pre>
T[B, T[B, T[B, T[B, E, 1, E], 2, T[B, E, 3, E]], 4, T[B, T[B, E, 5, E], 6, T[B, E, 7, E]]], 8, T[B, T[B, T[B, E, 9, E], 10, T[B, E, 11, E]], 12, T[B, T[B, E, 13, E], 14, T[B, E, 15, T[R, E, 16, E]]]]]
</pre>
 
=={{header|jq}}==
Line 990 ⟶ 1,074:
 
'''bindings.jq'''
<syntaxhighlight lang="jq"># bindings($x) attempts to match . and $x structurally on the
# assumption that . is free of JSON objects, and that any objects in
# $x will have distinct, singleton keys that are to be interpreted as
Line 1,020 ⟶ 1,104:
 
'''pattern-matching.jq'''
<syntaxhighlight lang="jq">include "bindings" {search: "."};
 
def E: []; # the empty node
Line 1,064 ⟶ 1,148:
{{out}}
For brevity and perhaps visual appeal, the output from jq has been trimmed as per the following invocation:
<syntaxhighlight lang="sh">jq -n -f pattern-matching.jq | grep -v '[][]' | tr -d ',"'</syntaxhighlight>
<pre>
Line 1,101 ⟶ 1,185:
=={{header|Julia}}==
Julia's multiple dispatch model is based on the types of a function's arguments, but does not look deeper into the function's array arguments for the types of their contents. Therefore we do multi-dispatch on the balance function but then use an if statement within the multiply dispatched functions to further match based on argument vector contents.
<syntaxhighlight lang="julia">import Base.length
 
abstract type AbstractColoredNode end
Line 1,179 ⟶ 1,263:
Whilst Kotlin supports algebraic data types (via 'sealed classes') and destructuring of data classes, pattern matching on them (via the 'when' expression) is currently limited to matching the type. Consequently the balance() function is not very pretty!
<syntaxhighlight lang="scala">// version 1.1.51
 
import Color.*
Line 1,270 ⟶ 1,354:
=={{header|Nim}}==
{{libheader|fusion/matching}}
<syntaxhighlight lang="nim">import fusion/matching
{.experimental: "caseStmtMacros".}
 
Line 1,317 ⟶ 1,401:
 
=={{header|OCaml}}==
<syntaxhighlight lang="ocaml">
type color = R | B
type 'a tree = E | T of color * 'a tree * 'a * 'a tree
Line 1,350 ⟶ 1,434:
To match multiple variables at once, we create temporary tuples with "#".
 
<syntaxhighlight lang="oz">fun {Balance Col A X B}
case Col#A#X#B
of b#t(r t(r A X B) Y C )#Z#D then t(r t(b A X B) Y t(b C Z D))
Line 1,386 ⟶ 1,470:
Each of the single letter variables declared right after $balanced, match an instance of $balanced, and if they succeed, store the result into the %+ hash.
 
<syntaxhighlight lang="perl">#!perl
use 5.010;
use strict;
Line 1,468 ⟶ 1,552:
There is no formal support for this sort of thing in Phix, but that's not to say that whipping
something up is likely to be particularly difficult, so let's give it a whirl.
<!--<syntaxhighlight lang=Phix"phix">(phixonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\Pattern_matching.exw
Line 1,612 ⟶ 1,696:
=={{header|Picat}}==
{{trans|Prolog}}
<syntaxhighlight lang=Picat"picat">main =>
T = e,
foreach (X in 1..10)
Line 1,670 ⟶ 1,754:
=={{header|PicoLisp}}==
{{trans|Prolog}}
<syntaxhighlight lang=PicoLisp"picolisp">(be color (R))
(be color (B))
 
Line 1,706 ⟶ 1,790:
(ins @X @S (T @ @A @Y @B)) )</syntaxhighlight>
Test:
<syntaxhighlight lang=PicoLisp"picolisp">: (? (insert 2 E @A) (insert 1 @A @B) (insert 3 @B @C))
@A=(T B E 2 E) @B=(T B (T R E 1 E) 2 E) @C=(T B (T R E 1 E) 2 (T R E 3 E))
-> NIL</syntaxhighlight>
Line 1,739 ⟶ 1,823:
Structural pattern matching was added to Python in version 3.10.
 
<syntaxhighlight lang="python">from __future__ import annotations
from enum import Enum
from typing import NamedTuple
Line 1,871 ⟶ 1,955:
{{trans|OCaml}}
 
<syntaxhighlight lang="racket">
#lang racket
 
Line 1,929 ⟶ 2,013:
{{works with|rakudo|2016.11}}
Raku doesn't have algebraic data types (yet), but it does have pretty good pattern matching in multi signatures.
<syntaxhighlight lang=perl6"raku" line>enum RedBlack <R B>;
 
multi balance(B,[R,[R,$a,$x,$b],$y,$c],$z,$d) { [R,[B,$a,$x,$b],$y,[B,$c,$z,$d]] }
Line 1,965 ⟶ 2,049:
 
An abstract pattern is recursively defined and may contain, among others, the following elements: Literal, VariableDeclaration, MultiVariable, Variable, List, Set, Tuple, Node, Descendant, Labelled, TypedLabelled, TypeConstrained. More explanation can be found in the [http://http://tutor.rascal-mpl.org/Courses/Rascal/Rascal.html#/Courses/Rascal/Patterns/Abstract/Abstract.html Documentation]. Some examples:
<syntaxhighlight lang="rascal">
// Literal
rascal>123 := 123
Line 2,037 ⟶ 2,121:
 
A concrete pattern is a quoted concrete syntax fragment that may contain variables. The syntax that is used to parse the concrete pattern may come from any module that has been imported in the module in which the concrete pattern occurs. Some examples of concrete patterns:
<syntaxhighlight lang="rascal">// Quoted pattern
` Token1 Token2 ... Tokenn `
// A typed quoted pattern
Line 2,052 ⟶ 2,136:
There are two variants of the PatternsWitchAction. When the subject matches Pattern, the expression Exp is evaluated and the subject is replaced with the result. Secondly, when the subject matches Pattern, the (block of) Statement(s) is executed. See below for some ColoredTree examples:
 
<syntaxhighlight lang="rascal">// Define ColoredTrees with red and black nodes and integer leaves
data ColoredTree = leaf(int N)
| red(ColoredTree left, ColoredTree right)
Line 2,097 ⟶ 2,181:
Regular expressions are noated between two slashes. Most normal regular expressions patterns are available, such as ., \n, \d, etc. Additionally, flags can be used to create case intensiveness.
 
<syntaxhighlight lang="rascal">rascal>/XX/i := "some xx";
bool: true
rascal>/a.c/ := "abc";
Line 2,105 ⟶ 2,189:
The nodes used for this example are taken from the Wikipedia example at: &nbsp;
[[https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#/media/File:Red-black_tree_example.svg red black tree, an example]]
<syntaxhighlight lang="rexx">/*REXX pgm builds a red/black tree (with verification & validation), balanced as needed.*/
parse arg nodes '/' insert /*obtain optional arguments from the CL*/
if nodes='' then nodes = 13.8.17 8.1.11 17.15.25 1.6 25.22.27 /*default nodes. */
Line 2,156 ⟶ 2,240:
{{trans|Haskell}}
This would be a horribly inefficient way to implement a Red-Black Tree in Rust as nodes are being allocated and deallocated constantly, but it does show off Rust's pattern matching.
<syntaxhighlight lang="rust">#![feature(box_patterns, box_syntax)]
use self::Color::*;
use std::cmp::Ordering::*;
Line 2,233 ⟶ 2,317:
of that object.
 
<syntaxhighlight lang="scala">class RedBlackTree[A](implicit ord: Ordering[A]) {
sealed abstract class Color
case object R extends Color
Line 2,282 ⟶ 2,366:
 
=={{header|Standard ML}}==
<syntaxhighlight lang="sml">
datatype color = R | B
datatype 'a tree = E | T of color * 'a tree * 'a * 'a tree
Line 2,311 ⟶ 2,395:
=={{header|Swift}}==
{{works with|Swift|2+}}
<syntaxhighlight lang="swift">enum Color { case R, B }
enum Tree<A> {
case E
Line 2,353 ⟶ 2,437:
 
Tailspin doesn't have type names, so here using a tag. Neither does it have destructuring (which seems to be posited in the problem statement). Arguably, pattern matching in Tailspin is more readable while still as concise.
<syntaxhighlight lang="tailspin">
processor RedBlackTree
data node <{VOID}|{colour: <='black'|='red'>, left: <node>, right: <node>, value: <> VOID}> local
Line 2,382 ⟶ 2,466:
end balance
templates ins&{into:}
when <?($into <´node´ ={}>)> do { colour: 'red', left: {}, value: $, right: {}} !
when <..$into.value::raw> do { $into..., left: $ -> ins&{into: $into.left}} -> balance !
otherwise { $into..., right: $ -> ins&{into: $into.right}} -> balance !
Line 2,411 ⟶ 2,495:
 
Tcl doesn't have algebraic types built-in, but they can be simulated using tagged lists, and a custom pattern matching control structure can be built:
<syntaxhighlight lang="tcl"># From http://wiki.tcl.tk/9547
package require Tcl 8.5
package provide datatype 0.1
Line 2,484 ⟶ 2,568:
We can then code our solution similar to Haskell:
 
<syntaxhighlight lang="tcl">datatype define Color = R | B
datatype define Tree = E | T color left val right
 
Line 2,514 ⟶ 2,598:
}
}</syntaxhighlight>
 
=={{header|TXR}}==
 
TXR Lisp has structural pattern matching on objects of all kinds, including structures. We define a red-black tree structure like this, with a BOA constructor (by-order of arguments) for convenience:
 
<syntaxhighlight lang="txrlisp">
(defstruct (rbnode color left right data) ()
color
left
right
data)
</syntaxhighlight>
 
The empty tree case is handled by the <code>nil</code> symbol, so in terms of algebraic types, the tree is a sum of <code>nil</code> and the <code>rbnode</code> struct type, and that struct type is a product type of several properties. For the <code>color</code> slot, we use the keyword symbols <code>:red</code> and <code>:black</code> which needs not be declared anywhere. <code>data</code> can be any value.
 
TXR Lisp's syntax for matching structures looks like this:
 
<syntaxhighlight lang="txrlisp">
@(struct time year @y month @m)
</syntaxhighlight>
 
This example matches a time structure instance, capturing the year as <code>y</code>
and month as <code>m</code>.
 
Structures aren't ordered tuples; they are clumps of of named slots,
that cannot be accessed by position. This would break under
inheritance, in particular multiple inheritance.
 
Furthermore, variables have the <code>@</code> sigil in most pattern matching
constructs, because symbols without the sigil denote themselves as literal
patterns. The pattern <code>x</code> matches the symbol <code>x</code>
literally, and no other object. The pattern <code>@x</code> matches any
object and captures it as <code>x</code>.
 
These above features make it verbose and somewhat noisy to express
pattern matching of our <code>rbtree</code> node. However, TXR Lisp's
pattern matching sublanguage supports application-defined macro patterns,
defined by the <code>defmatch</code> macro. With these we can achieve
a shorthand notation which matches nodes as if they were ordered tuples,
and which drops the sigils from variables.
 
<syntaxhighlight lang="txrlisp">
(defmatch rb (color left right data)
(flet ((var? (sym) (if (bindable sym) ^@,sym sym)))
^@(struct rbnode
color ,(var? color)
left ,(var? left)
right ,(var? right)
data ,(var? data))))
 
(defmatch red (left right data)
^@(rb :red ,left ,right ,data))
 
(defmatch black (left right data)
^@(rb :black ,left ,right ,data))
</syntaxhighlight>
 
And with all the above, we can write the code like this:
 
<syntaxhighlight lang="txrlisp">
(defun-match rb-balance
((@(or @(black @(red @(red a b x) c y) d z)
@(black @(red a @(red b c x) x) d z)
@(black a @(red @(red b c y) d z) x)
@(black a @(red b @(red c d z) y) x)))
(new (rbnode :red
(new (rbnode :black a b x))
(new (rbnode :black c d z))
y)))
((@else) else))
 
(defun rb-insert-rec (tree x)
(match-ecase tree
(nil
(new (rbnode :red nil nil x)))
(@(rb color a b y)
(cond
((< x y)
(rb-balance (new (rbnode color (rb-insert-rec a) b y))))
((> x y)
(rb-balance (new (rbnode color a (rb-insert-rec b) y))))
(t tree)))))
 
(defun rb-insert (tree x)
(match-case (rb-insert-rec tree x)
(@(red a b y) (new (rbnode :black a b y)))
(@else else)))
</syntaxhighlight>
 
Insertion is split into two functions: a recursive one which works on its own, except that whenever the tree ends up with a red root, we would like to rewrite that node to a black one. We make the insertion function call the recursive one and then do this fix-up using pattern matching again.
 
=={{header|Wren}}==
{{trans|Go}}
Wren doesn't have either algebraic data types or pattern matching though, despite that, the ''T.balance()'' method looks better than I thought it would :)
<syntaxhighlight lang=ecmascript"wren">var R = "R"
var B = "B"
 
Line 2,611 ⟶ 2,785:
{{omit from|BBC BASIC}}
{{omit from|C}}
{{omit from|Java}}
{{omit from|Pascal}}
{{omit from|Processing}}
Anonymous user