Hashing functions; builtins hash, uniq and unique

This commit is contained in:
Robert Bendun 2022-09-05 23:17:40 +02:00
parent ab0ba8d4c8
commit d77f844c81
7 changed files with 112 additions and 0 deletions

View File

@ -1099,4 +1099,9 @@ static constexpr bool is_callable(Value::Type type)
Result<std::vector<Value>> flatten(Interpreter &i, std::span<Value>); Result<std::vector<Value>> flatten(Interpreter &i, std::span<Value>);
Result<std::vector<Value>> flatten(Interpreter &i, std::vector<Value>); Result<std::vector<Value>> flatten(Interpreter &i, std::vector<Value>);
template<> struct std::hash<Token> { std::size_t operator()(Token const&) const; };
template<> struct std::hash<Ast> { std::size_t operator()(Ast const&) const; };
template<> struct std::hash<Number> { std::size_t operator()(Number const&) const; };
template<> struct std::hash<Value> { std::size_t operator()(Value const&) const; };
#endif #endif

View File

@ -97,4 +97,7 @@ struct Interpreter::Incoming_Midi_Callbacks
enum class Midi_Connection_Type { Output, Input }; enum class Midi_Connection_Type { Output, Input };
Result<void> ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name); Result<void> ensure_midi_connection_available(Interpreter&, Midi_Connection_Type, std::string_view operation_name);
constexpr std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
}
#endif #endif

View File

@ -4,6 +4,7 @@
#include <random> #include <random>
#include <memory> #include <memory>
#include <iostream> #include <iostream>
#include <unordered_set>
void Interpreter::register_callbacks() void Interpreter::register_callbacks()
{ {
@ -601,6 +602,48 @@ static Result<Value> builtin_rotate(Interpreter &i, std::vector<Value> args)
return Value::from(std::move(array)); return Value::from(std::move(array));
} }
/// Returns unique collection of arguments
static Result<Value> builtin_unique(Interpreter &i, std::vector<Value> args)
{
auto array = Try(flatten(i, args));
std::unordered_set<Value> seen;
std::vector<Value> result;
for (auto &el : array) {
if (!seen.contains(el)) {
seen.insert(el);
result.push_back(std::move(el));
}
}
return Value::from(std::move(result));
}
/// Returns arguments with all successive copies eliminated
static Result<Value> builtin_uniq(Interpreter &i, std::vector<Value> args)
{
auto array = Try(flatten(i, args));
std::optional<Value> previous;
std::vector<Value> result;
for (auto &el : array) {
if (previous && *previous == el)
continue;
result.push_back(el);
previous = std::move(el);
}
return Value::from(std::move(result));
}
static Result<Value> builtin_hash(Interpreter&, std::vector<Value> args)
{
return Value::from(Number(
std::accumulate(args.cbegin(), args.cend(), size_t(0), [](size_t h, Value const& v) {
return hash_combine(h, std::hash<Value>{}(v));
})
));
}
/// Build chord from arguments /// Build chord from arguments
static Result<Value> builtin_chord(Interpreter &i, std::vector<Value> args) static Result<Value> builtin_chord(Interpreter &i, std::vector<Value> args)
{ {
@ -714,6 +757,7 @@ void Interpreter::register_builtin_functions()
global.force_define("flat", builtin_flat); global.force_define("flat", builtin_flat);
global.force_define("floor", apply_numeric_transform<&Number::floor>); global.force_define("floor", apply_numeric_transform<&Number::floor>);
global.force_define("for", builtin_for); global.force_define("for", builtin_for);
global.force_define("hash", builtin_hash);
global.force_define("if", builtin_if); global.force_define("if", builtin_if);
global.force_define("incoming", builtin_incoming); global.force_define("incoming", builtin_incoming);
global.force_define("instrument", builtin_program_change); global.force_define("instrument", builtin_program_change);
@ -738,6 +782,8 @@ void Interpreter::register_builtin_functions()
global.force_define("sort", builtin_sort); global.force_define("sort", builtin_sort);
global.force_define("try", builtin_try); global.force_define("try", builtin_try);
global.force_define("typeof", builtin_typeof); global.force_define("typeof", builtin_typeof);
global.force_define("uniq", builtin_uniq);
global.force_define("unique", builtin_unique);
global.force_define("up", builtin_range<Range_Direction::Up>); global.force_define("up", builtin_range<Range_Direction::Up>);
global.force_define("update", builtin_update); global.force_define("update", builtin_update);
} }

View File

@ -1,4 +1,5 @@
#include <musique.hh> #include <musique.hh>
#include <musique_internal.hh>
#include <iomanip> #include <iomanip>
@ -277,3 +278,9 @@ std::string_view type_name(Token::Type type)
} }
unreachable(); unreachable();
} }
std::size_t std::hash<Token>::operator()(Token const& token) const
{
return hash_combine(std::hash<std::string_view>{}(token.source), size_t(token.type));
}

View File

@ -1,4 +1,6 @@
#include <musique.hh> #include <musique.hh>
#include <musique_internal.hh>
#include <cmath> #include <cmath>
#include <numeric> #include <numeric>
#include <charconv> #include <charconv>
@ -332,3 +334,9 @@ Result<Number> Number::pow(Number n) const
// We need to protect ourselfs against even roots of negative numbers. // We need to protect ourselfs against even roots of negative numbers.
unimplemented(); unimplemented();
} }
std::size_t std::hash<Number>::operator()(Number const& value) const
{
std::hash<Number::value_type> h;
return hash_combine(h(value.num), h(value.den));
}

View File

@ -1,5 +1,8 @@
#include <musique.hh> #include <musique.hh>
#include <musique_internal.hh>
#include <iostream> #include <iostream>
#include <numeric>
static Ast wrap_if_several(std::vector<Ast> &&ast, Ast(*wrapper)(std::vector<Ast>)); static Ast wrap_if_several(std::vector<Ast> &&ast, Ast(*wrapper)(std::vector<Ast>));
@ -616,3 +619,12 @@ void dump(Ast const& tree, unsigned indent)
std::cout << std::flush; std::cout << std::flush;
} }
} }
std::size_t std::hash<Ast>::operator()(Ast const& value) const
{
auto h = std::hash<Token>{}(value.token);
h = std::accumulate(value.arguments.begin(), value.arguments.end(), h, [this](size_t h, Ast const& node) {
return hash_combine(h, operator()(node));
});
return hash_combine(size_t(value.type), h);
}

View File

@ -1,4 +1,7 @@
#include <musique.hh> #include <musique.hh>
#include <musique_internal.hh>
#include <numeric>
/// Finds numeric value of note. This form is later used as in /// Finds numeric value of note. This form is later used as in
/// note to midi resolution in formula octave * 12 + note_index /// note to midi resolution in formula octave * 12 + note_index
@ -522,3 +525,31 @@ Result<std::vector<Value>> flatten(Interpreter &i, std::vector<Value> args)
{ {
return flatten(i, std::span(args)); return flatten(i, std::span(args));
} }
std::size_t std::hash<Value>::operator()(Value const& value) const
{
size_t value_hash = 0;
switch (value.type) {
break; case Value::Type::Nil: value_hash = 0;
break; case Value::Type::Number: value_hash = std::hash<Number>{}(value.n);
break; case Value::Type::Symbol: value_hash = std::hash<std::string>{}(value.s);
break; case Value::Type::Bool: value_hash = std::hash<bool>{}(value.b);
break; case Value::Type::Intrinsic: value_hash = ptrdiff_t(value.intr);
break; case Value::Type::Block: value_hash = hash_combine(std::hash<Ast>{}(value.blk.body), value.blk.parameters.size());
break; case Value::Type::Array:
value_hash = std::accumulate(value.array.elements.begin(), value.array.elements.end(), value_hash, [this](size_t h, Value const& v) {
return hash_combine(h, operator()(v));
});
break; case Value::Type::Music:
value_hash = std::accumulate(value.chord.notes.begin(), value.chord.notes.end(), value_hash, [](size_t h, Note const& n) {
h = hash_combine(h, n.base);
h = hash_combine(h, std::hash<std::optional<Number>>{}(n.length));
h = hash_combine(h, std::hash<std::optional<i8>>{}(n.octave));
return h;
});
}
return hash_combine(value_hash, size_t(value.type));
}