Adding a custom type to a RedBlackTree - d

I want to keep an ordered set of records and the standard provides me with RedBlackTree. The record is of type Tuple!(string, uint). Here's what it looks like:
import std.json : parseJSON;
uint[string] wordTable;
import std.datetime.stopwatch : StopWatch, AutoStart;
auto sw = StopWatch(AutoStart.yes);
const auto j = parseJSON(get(link));
const long downloadTime = sw.peek.total!"msecs";
import std.typecons : Tuple, tuple;
import std.container.rbtree : RedBlackTree;
import std.functional : binaryFun;
RedBlackTree!(Tuple!(string, uint), binaryFun!("a[1] > b[1]")) records;
foreach (node; j["posts"].array()) {
import std.stdio : writeln;
import std.utf : decode;
if ("com" in node) {
import std.algorithm : splitter;
foreach (word; getStr(node["com"].str()).splitter(' ')) {
import std.string : strip;
if (word.strip().length > 0)
wordTable.require(word, 0)++;
records ~= (tuple(word, wordTable[word])); // error
}
}
}
Now primarily I had used insert() method to add a record to the records but it causes segfault in runtime. So I decided to use ~= in hopes for better error messages. And here's what the compiler says:
Error: cannot append type Tuple!(string, uint) to type std.container.rbtree.RedBlackTree!(Tuple!(string, uint), binaryFun, false)
According to https://dlang.org/phobos/std_container_rbtree.html#.RedBlackTree I have to provide a type such that calling less(a, b) on it returns a boolean. So I went ahead and created a type for it:
struct Record {
string key;
uint value;
int opCmp(ref const Record other) const {
return value - other.value;
}
}
// bool less(Record a, Record b) {
// return a < b;
// }
void main(string[] args) {
import std.stdio : writeln, writefln;
if (args.length < 3) {
writeln("Must have 2 arguments " ~ "first argument is the link, "
~ "the second one is for minimum repeatation threshold. Exiting.");
import core.stdc.stdlib : exit;
exit(-1);
}
const auto link = parseLink(args[1]);
const auto threshold = atoui(args[2]);
import std.json : parseJSON;
uint[string] wordTable;
import std.datetime.stopwatch : StopWatch, AutoStart;
auto sw = StopWatch(AutoStart.yes);
const auto j = parseJSON(get(link));
const long downloadTime = sw.peek.total!"msecs";
import std.container.rbtree : RedBlackTree;
import std.functional : binaryFun;
RedBlackTree!Record records;
foreach (node; j["posts"].array()) {
import std.utf : decode;
if ("com" in node) {
import std.algorithm : splitter;
foreach (word; getStr(node["com"].str()).splitter(' ')) {
import std.string : strip;
if (word.strip().length > 0)
wordTable.require(word, 0)++;
records ~= (Record(word, wordTable[word]));
}
}
}
This time the compiler complains:
Error: cannot append type Record to type std.container.rbtree.RedBlackTree!(Record, "a < b", false)
So the gist of the question is, if I have an RedBlackTree with a custom binaryFun, how can I add an instance of a tuple or a custom type to it?

Related

Can I implement operator overloading for D's SumType alias?

TLDR: Is there a way make D's SumType play nice with opCmp while maintaining its functionality?
Context
I'm writing a program for which D's native SumType works almost completely. However, I would like to be able to do the following:
alias Foo = SumType!(int, string);
Foo x = 3;
Foo y = 5;
writeln(max(x, y));
However, since no ordering is natively defined for SumType, I receive the following error:
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\comparison.d(1644): Error: static assert: "Invalid arguments: Cannot compare types SumType!(int, string) and SumType!(int, string) for ordering."
mwe.d(11): instantiated from here: `max!(SumType!(int, string), SumType!(int, string))`
I was able to remedy this specific issue using the following method:
import std.stdio : writeln;
import std.exception : assertThrown;
import std.algorithm.comparison : max;
import core.exception : AssertError;
import std.sumtype;
struct Foo {
SumType!(int, string) value;
this(T)(T v) {
value = v;
}
ref Atom opAssign(T)(T rhs) {
value = rhs;
return this;
}
int opCmp(Foo other) {
return match!(
(a, b) => a < b ? -1 : a == b ? 0 : 1,
(_1, _2) => assert(0, "Cannot match")
)(value, other.value);
}
}
void main() {
Foo x = 3;
Foo y = 7;
Foo z = "asdf";
assert(x < y); // comparing ints works correctly
assertThrown!AssertError(x < z); // cannot compare int and string
assert(max(x, y) == y); // D's max works
}
The Problem
While I can now use x.value.match!(...) where I used to use x.match!(...), I would like to still be able to call .match! directly on x, and also use match!(...)(x, y) instead of match!(...)(x.value, y.value). I do not like the idea of inserting hundreds of .value throughout my code just to make certain functions like max work, and would prefer if there were a more elegant solution. I tried tinkering around with defining a custom opDispatch using mixins but I couldn't get that to play nicely with the existing SumType:
struct Foo {
SumType!(int, string) value;
this(T)(T v) {
value = v;
}
ref Atom opAssign(T)(T rhs) {
value = rhs;
return this;
}
int opCmp(Foo other) {
return match!(
(a, b) => a < b ? -1 : a == b ? 0 : 1,
(_1, _2) => assert(0, "Cannot match")
)(value, other.value);
}
auto opDispatch(string name, T...)(T vals) {
return mixin("value." ~ name)(vals);
}
}
void main() {
Foo y = 7;
y.match!(
(int intValue) => writeln("Received an integer"),
(string strValue) => writeln("Received a string")
);
}
And I am unable to decode the error which results:
mwe.d(38): Error: none of the overloads of template `std.sumtype.match!(function (int intValue) #safe
{
writeln("Received an integer");
return ;
}
, function (string strValue) #safe
{
writeln("Received a string");
return ;
}
).match` are callable using argument types `!()(Foo)`
C:\D\dmd2\windows\bin\..\..\src\phobos\std\sumtype.d(1659): Candidate is: `match(SumTypes...)(auto ref SumTypes args)`
with `SumTypes = (Foo)`
must satisfy the following constraint:
` allSatisfy!(isSumType, SumTypes)`
Beyond that I am out of ideas as to how to find a less clunky solution.
I suggest giving alias this a try. Similar to class inheritance, this lets you specialize a type and let other things fall back to the original member.
import std.stdio : writeln;
import std.exception : assertThrown;
import std.algorithm.comparison : max;
import core.exception : AssertError;
import std.sumtype;
struct Foo {
SumType!(int, string) value;
this(T)(T v) {
value = v;
}
int opCmp(Foo other) {
return match!(
(a, b) => a < b ? -1 : a == b ? 0 : 1,
(_1, _2) => assert(0, "Cannot match")
)(value, other.value);
}
alias value this;
}
void main() {
Foo x = 3;
Foo y = 7;
Foo z = "asdf";
assert(x < y); // comparing ints works correctly
assertThrown!AssertError(x < z); // cannot compare int and string
assert(max(x, y) == y); // D's max works
// this will now automatically fall back to y.value.match
y.match!(
(int intValue) => writeln("Received an integer"),
(string strValue) => writeln("Received a string")
);
}
See, you still must construct your special type, but then after that, it will look up there for members. It will find the opCmp, letting it extend the type. But then for everything else, since it isn't there, it will try checking obj.value instead, falling back to the original type.
This doesn't always work, and it means it will implicitly convert too, meaning you can pass a Foo to a void thing(SumType!(int, string)) with it passing foo.value to the function, which may or may not be desirable.
But I think it is the closest thing to what you want here.
(note btw why you got an error originally is that match isn't actually a member of SumType. it is an outside free function that takes all the match lambdas as template arguments. An opDispatch could forward template arguments too - it can be done in a two-level definition - but since match is not a member anyway, it isn't quite going to solve things anyway whereas the alias this does seem to work)

Can't initialize D struct member with Range

I'm attempting to learn D and ran into an issue with structs and initialization. When the following code is compiled as rdmd -version=templat code.d, I get a bunch of errors like:
> dmd -version=templat code.d
D:\D\dmd2\windows\bin\..\..\src\phobos\std\random.d(1610): Error: static variable initialized cannot be read at compile time
D:\D\dmd2\windows\bin\..\..\src\phobos\std\random.d(1653): called from here: rndGen()
D:\D\dmd2\windows\bin\..\..\src\phobos\std\random.d(1653): called from here: uniform(a, b, rndGen())
code.d(8): called from here: uniform(1u, 7u)
D:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(3470): called from here: (*function () => uniform(1u, 7u))()
D:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(3387): called from here: gen.popFront()
code.d(8): called from here: generate()
code.d(13): Error: template instance `code.Dice!(1u, 7u)` error instantiating
I assume this has something to do with needing to be able to statically resolve the uniform(Lo, Hi). But I am at a loss as to how solve this issue. When I compile with rdmd -version=variabl code.d, I encounter no issues.
For what it's worth, my goal is to be able to define a "dice" type so that I can implement ranges, operator overloading, etc. on it to get a feel for this in D.
import std.range : generate ;
import std.random : uniform ;
version(templat)
{
struct Dice(uint Lo, uint Hi)
{
auto values = generate!(() => uniform(Lo, Hi));
}
void main()
{
Dice!(1, 7) d6;
}
}
version(variabl)
{
void main()
{
auto d6a = generate!(() => uniform(1, 7));
}
}
In order for generate! to work it needs to cache the first result when it's constructed, but setting a default struct values happens at compile-time. (so tries to run uniform() using ctfe...)
What you could do is use alias like this:
import std.range : take, generate;
import std.random : uniform;
import std.stdio, writeln;
alias Dice(uint Lo, uint Hi) = generate!(() => uniform(Lo, Hi));
void main()
{
auto d6 = Dice!(1, 7);
writeln(d6.front);
writeln(d6.front); // same as above because you didnt pop it
d6.popFront();
writeln(d6.front);
d6.popFront();
d6.take(3).writeln;
}
Also, here is an example of createing a range directly without useing generate!.
import std.range : take;
import std.random : uniform;
import std.stdio, writeln;
struct Dice(uint Lo, uint Hi)
{
void popFront()
{
front = uniform(Lo, Hi);
}
static bool empty = false;
uint front;
}
auto makeDice(uint Lo, uint Hi)(){
auto dice = Dice!(Lo, Hi)();
dice.popFront();
return dice;
}
void main()
{
auto d6 = makeDice!(1, 7);
writeln(d6.front);
writeln(d6.front); // same as above because you didnt pop it
d6.popFront();
writeln(d6.front);
d6.popFront();
d6.take(3).writeln;
}

Using enum with std.son.JSONValue

How can enum be used with std.json.JSONValue in D? The implementing code does not have access to the module types and to! cannot be used, that's the reason for the CommonType!(OriginalType!()) call.
import std.json;
import std.stdio;
import std.traits;
enum Kind : string
{
dog = "Dog",
cat = "Cat",
pony = "Pony"
};
unittest
{
writeln("TEST: std.json.JSONValue for enum");
Kind animal = Kind.dog;
Kind[] pets = [Kind.cat, Kind.dog];
static if (is(typeof(animal) == enum))
{
writeln("enum");
}
//JSONValue a = JSONValue(animal); // <-- Assertion failed: (0), function unqualify, file mtype.c, line 2022.
JSONValue a = JSONValue(cast(CommonType!(OriginalType!(typeof(animal))))animal); // <-- this is OK
static if (is(typeof(pets) == enum))
{
writeln("array of enum");
}
//JSONValue p = JSONValue(pets); // <-- Assertion failed: (0), function unqualify, file mtype.c, line 2022.
//JSONValue p = JSONValue(???);
}
You can use convert your enum values to strings and back using std.conv.to.
import std.json;
import std.conv : to;
import std.algorithm : map, equal;
...
Kind animal = Kind.dog;
Kind[] pets = [Kind.cat, Kind.dog];
// convert Kind to string and store in JSONValue
JSONValue json1 = animal.to!string;
// extract string from JSONValue convert to Kind
assert(animal == json1.str.to!Kind);
JSONValue json2 = pets.to!(string[]);
// map JSONValues to strings, then Kinds
auto pets2 = json2.array.map!(x => x.str.to!Kind);
assert(pets2.equal(pets));
Using string, the JSON values would be represented as strings matching the enum names. You could instead use int or anything else that can be converted to both your enum type and a JSONValue.

calling a java method in velocity template

I have a class CurrencyUtil in which I have written a method convertCurrency(String symbol, long value) I want to call this method from velocity template. I am putting object of this class map.put("formatter", currencyUtil); and in template I am using the tag as $formatter.convertCurrency($currency, $total) but when the template is rendered it is not printing the result.
here my question is that, should the parameter names in the java method and in template be same? or is there any other problem?
Parameter names in java method and template can be different. You may try to locate your problem using the following example.
Example.java
package com.example.currency;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.Template;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import java.io.*;
public class Example
{
public Example(String templateFile)
{
try
{
Velocity.init("velocity.properties");
VelocityContext context = new VelocityContext();
CurrencyUtil cu = new CurrencyUtil();
cu.setCurrencyRate("EUR", 1.25);
context.put("formatter", cu);
Template template = null;
try
{
template = Velocity.getTemplate(templateFile);
}
catch( ResourceNotFoundException rnfe )
{
System.out.println("Example : error : cannot find template " + templateFile );
}
catch( ParseErrorException pee )
{
System.out.println("Example : Syntax error in template " + templateFile + ":" + pee );
}
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(System.out));
if ( template != null)
template.merge(context, writer);
writer.flush();
writer.close();
}
catch( Exception e )
{
System.out.println(e);
}
}
public static void main(String[] args)
{
Example t = new Example("example.vm");
}
}
CurrencyUtil.java
package com.example.currency;
import java.util.Map;
import java.util.HashMap;
public class CurrencyUtil {
private static Map<String, Double> rates = new HashMap<String, Double>();
public double getCurrencyRate(String symbol){
return rates.get(symbol);
}
public void setCurrencyRate(String symbol, double currencyRate){
rates.put(symbol, currencyRate);
}
public double convertCurrency(String symbol, long value){
return value * getCurrencyRate(symbol);
}
}
example.vm
#set( $total = 10000000000)
#set( $currency = "EUR")
$formatter.convertCurrency($currency, $total)
Velocity receives all parameters as Strings. In your function you receive a String and apply a cast.

JavaFX - bind property to properties of every element in observable Collection

Does exist any method which bind BooleanProperty to conjunction of every element in ObservableList?
ObservableList<BooleanProperty> list;
list = FXCollections.observableList(new ArrayList<BooleanProperty>));
BooleanProperty emptyProperty = new SimpleBooleanProperty();
emptyProperty.bind(Bindings.conunction(list));`
Is there such a method as:
static BooleanBinding conjunction(ObservableList<BooleanProperty> op)
There is no conjunction api defined in the JavaFX 2.2 platform.
You could create a ConjunctionBooleanBinding (aka AllTrueBinding) by subclassing BooleanBinding.
Accept the ObservableList in the constructor of your new class, and use the low level binding api in an overridden computeValue method to set the binding value based upon logically anding together all of the boolean values in the list.
Here is a sample implementation. The sample could be further performance optimized and make use of WeakReferences, so it does not require manual disposition.
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.collections.*;
public class AllTrueBinding extends BooleanBinding {
private final ObservableList<BooleanProperty> boundList;
private final ListChangeListener<BooleanProperty> BOUND_LIST_CHANGE_LISTENER =
new ListChangeListener<BooleanProperty>() {
#Override public void onChanged(
ListChangeListener.Change<? extends BooleanProperty> change
) {
refreshBinding();
}
};
private BooleanProperty[] observedProperties = {};
AllTrueBinding(ObservableList<BooleanProperty> booleanList) {
booleanList.addListener(BOUND_LIST_CHANGE_LISTENER);
boundList = booleanList;
refreshBinding();
}
#Override protected boolean computeValue() {
for (BooleanProperty bp: observedProperties) {
if (!bp.get()) {
return false;
}
}
return true;
}
#Override public void dispose() {
boundList.removeListener(BOUND_LIST_CHANGE_LISTENER);
super.dispose();
}
private void refreshBinding() {
super.unbind(observedProperties);
observedProperties = boundList.toArray(new BooleanProperty[0]);
super.bind(observedProperties);
this.invalidate();
}
}
And here is a test harness to demonstrate how it works:
import java.util.*;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class ListBindingTest {
final BooleanProperty a = new SimpleBooleanProperty(true);
final BooleanProperty b = new SimpleBooleanProperty(true);
final BooleanProperty c = new SimpleBooleanProperty(true);
final BooleanProperty d = new SimpleBooleanProperty(true);
final ObservableList<BooleanProperty> booleanList =
FXCollections.observableArrayList(a, b, c, d);
public static void main(String[] args) {
new ListBindingTest().test();
}
private void test() {
AllTrueBinding at = new AllTrueBinding(booleanList);
System.out.println(at.get() + forArrayString(booleanList));
b.set(false);
System.out.println(at.get() + forArrayString(booleanList));
b.set(true);
System.out.println(at.get() + forArrayString(booleanList));
booleanList.add(new SimpleBooleanProperty(false));
System.out.println(at.get() + forArrayString(booleanList));
booleanList.remove(3, 5);
System.out.println(at.get() + forArrayString(booleanList));
at.dispose();
}
private String forArrayString(List list) {
return " for " + Arrays.toString(list.toArray());
}
}
You can easily implement the method as follows:
public static BooleanBinding conjunction(ObservableList<BooleanProperty> list){
BooleanBinding and = new SimpleBooleanProperty(true).and(list.get(0));
for(int i = 1; i < list.size(); i++){
and = and.and(list.get(i));
}
return and;
}