Create an object/Native demonstration: Difference between revisions

m
→‎{{header|Wren}}: Changed to Wren S/H
(Updated D entry)
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(50 intermediate revisions by 22 users not shown)
Line 1:
{{draft task}}
 
Create a Hash/Associative Array/Dictionary-like object, that can be initialized with some default key/value pairs using the languages native method of object creation. The object should behave like a native Hash/Associative Array/Dictionary of the language, if any, but with the following differences:
 
* No new itemkeys can be added;
* Keys cannot be removed;
* Item cannot be deleted, (but native delete method may used to reset the item's value to default) ;
* Attempting to delete a key should set that keys value back to that used during initialisation.
(The value assigned to keys may be changed by normal assignment however).
 
If the language supports '''Magic Methods''', then show how these work.
 
=={{header|BASIC}}==
==={{header|BASIC256}}===
<syntaxhighlight lang="basic256">map mapa
mapa["A"] = 65
mapa["B"] = 66
mapa["C"] = 67
 
foreach valor in mapa
print valor
print mapa[valor]
next valor</syntaxhighlight>
{{out}}
<pre>A
65
B
66
C
67</pre>
 
==={{header|FreeBASIC}}===
FB doesn't have Dict natively, but we can implement them via Type
<syntaxhighlight lang="freebasic">Type dict
m1 As String*1
m2 As Integer
End Type
 
Dim mapOf(1 To 3) As dict => {("A", 65), ("B", 66), ("C", 67)}
 
For i As Integer = 1 To Ubound(mapOf)
Print mapOf(i).m1
Print mapOf(i).m2
Next i</syntaxhighlight>
{{out}}
<pre>A
65
B
66
C
67</pre>
 
=={{header|C++}}==
<syntaxhighlight lang="cpp">#include <iostream>
#include <map>
#include <utility>
 
using namespace std;
 
template<typename T>
class FixedMap : private T
{
// Two standard maps are used to implement FixedMap. One as a private
// base class which will allow the values (but not the keys) to be modified.
// Members of a private base class are not exposed to the derived class which will
// prevent keys from being added or deleted. Another map will hold copies of
// the initial values.
T m_defaultValues;
public:
FixedMap(T map)
: T(map), m_defaultValues(move(map)){}
// Expose members of the base class that do not modify the map.
using T::cbegin;
using T::cend;
using T::empty;
using T::find;
using T::size;
 
// Also expose members that can modify values but not add or remove keys.
using T::at;
using T::begin;
using T::end;
// The [] operator will normally add a new key if the key is not already in the
// map. Instead, throw an error if the key is missing.
auto& operator[](typename T::key_type&& key)
{
// Make it behave like at()
return this->at(forward<typename T::key_type>(key));
}
// Instead of removing a key, change the sematics of erase() to restore
// the original value of the key.
void erase(typename T::key_type&& key)
{
T::operator[](key) = m_defaultValues.at(key);
}
 
// Also change the sematics of clear() to restore all keys
void clear()
{
// Reset the base class using the defaults
T::operator=(m_defaultValues);
}
};
 
// Print the contents of a map
auto PrintMap = [](const auto &map)
{
for(auto &[key, value] : map)
{
cout << "{" << key << " : " << value << "} ";
}
cout << "\n\n";
};
 
int main(void)
{
// Create a fixed map based on the standard map
cout << "Map intialized with values\n";
FixedMap<map<string, int>> fixedMap ({
{"a", 1},
{"b", 2}});
PrintMap(fixedMap);
cout << "Change the values of the keys\n";
fixedMap["a"] = 55;
fixedMap["b"] = 56;
PrintMap(fixedMap);
cout << "Reset the 'a' key\n";
fixedMap.erase("a");
PrintMap(fixedMap);
cout << "Change the values the again\n";
fixedMap["a"] = 88;
fixedMap["b"] = 99;
PrintMap(fixedMap);
cout << "Reset all keys\n";
fixedMap.clear();
PrintMap(fixedMap);
try
{
// Adding or retrieving a missing key is a run time error
cout << "Try to add a new key\n";
fixedMap["newKey"] = 99;
}
catch (exception &ex)
{
cout << "error: " << ex.what();
}
}
</syntaxhighlight>
{{out}}
<pre>Map intialized with values
{a : 1} {b : 2}
 
Change the values of the keys
{a : 55} {b : 56}
 
Reset the 'a' key
{a : 1} {b : 56}
 
Change the values the again
{a : 88} {b : 99}
 
Reset all keys
{a : 1} {b : 2}
 
Try to add a new key
error: map::at</pre>
 
=={{header|D}}==
{{trans|Python}}
<langsyntaxhighlight lang="d">struct DefaultAA(TK, TV) {
TV[TK] standard, current;
 
this(TV[TK] default_) pure /*nothrow*/ @safe {
this.standard = default_;
this.current = default_.dup;
Line 24 ⟶ 191:
}
 
void clear() /*pure /*nothrow*/ @safe {
current = standard.dup;
}
Line 31 ⟶ 198:
void main() {
import std.stdio;
auto d = DefaultAA!(string, int)(["a": 1, "b": 2].DefaultAA!(string, int);
 
writeln(d).writeln; // ["a":1, "b":2]
d["a"] = 55; d["b"] = 66;
writeln(d).writeln; // ["a":55, "b":66]
d.clear();
writeln(d).writeln; // ["a":1, "b":2]
d["a"] = 55; d["b"] = 66;
writeln(d["a"]).writeln; // 55
d.remove("a");
writeln(d).writeln; // ["a":1, "b":66]
}</langsyntaxhighlight>
{{out}}
<pre>["a":1, "b":2]
Line 49 ⟶ 216:
55
["a":1, "b":66]</pre>
 
=={{header|Go}}==
Go's built-in map type is mutable and so, to complete this task, we need to create a read-only wrapper for it which doesn't permit further items to be added or existing items to be deleted though does allow them to be reset to their default value.
 
First create a sub-directory, romap, of the project directory and place the following package in it:
<syntaxhighlight lang="go">package romap
 
type Romap struct{ imap map[byte]int }
 
// Create new read-only wrapper for the given map.
func New(m map[byte]int) *Romap {
if m == nil {
return nil
}
return &Romap{m}
}
 
// Retrieve value for a given key, if it exists.
func (rom *Romap) Get(key byte) (int, bool) {
i, ok := rom.imap[key]
return i, ok
}
 
// Reset value for a given key, if it exists.
func (rom *Romap) Reset(key byte) {
_, ok := rom.imap[key]
if ok {
rom.imap[key] = 0 // default value of int
}
}</syntaxhighlight>
 
This package can now be imported and used within the main package as follows:
<syntaxhighlight lang="go">package main
 
import (
"./romap"
"fmt"
)
 
func main() {
// create a normal map
m := map[byte]int{'A': 65, 'B': 66, 'C': 67}
 
// place it in a read-only wrapper so no new item can be added or item deleted.
rom := romap.New(m)
 
// retrieve value represented by 'C' say
i, _ := rom.Get('C')
fmt.Println("'C' maps to", i)
 
// reset this to default value (doesn't actually delete the key)
rom.Reset('C')
i, _ = rom.Get('C')
fmt.Println("'C' now maps to", i)
}</syntaxhighlight>
 
{{out}}
<pre>
'C' maps to 67
'C' now maps to 0
</pre>
 
=={{header|J}}==
Line 54 ⟶ 282:
Given a list of keys and an associated list of values, the idiomatic way of expressing this concept in J would be:
 
<langsyntaxhighlight lang="j">lookup=: values {~ keys&i.</langsyntaxhighlight>
 
For example:
 
<langsyntaxhighlight lang="j"> lookup=: 10 20 30 40 50 {~ (;:'this is a test')&i.
lookup ;:'a test'
30 40</langsyntaxhighlight>
 
Notes:
Line 69 ⟶ 297:
 
3) In J, objects are always referenced, but all data is passed by value. This means that objects can never be passed to a function -- only a reference to an object (its name) can be passed. This means that objects exist only in the way things are named, in J. So for the most part, we do not call things "objects" in J, and this task has nothing to do with what are called "objects" in J. However, this does demonstrate how things are created in J -- you write their definition, and can use them and/or assign to names or inspect them or whatever else.
 
=={{header|Java}}==
 
Java supports unmodifiable maps, sets, lists, and other more specialized unmodifiable collections. In this example, we have a unmodifiable map. We first create an ordinary map, modify as needed, then call the <code>Collections.unmodifiableMap</code>. We can subsequently read the map, but modification is not permitted. The returned map will subsequently throw a <code>UnsupportedOperationException</code> exception if a mutation operator is called. Several are demonstrated below.
 
<syntaxhighlight lang="java">
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
 
// Title: Create an object/Native demonstration
 
public class ImmutableMap {
 
public static void main(String[] args) {
Map<String,Integer> hashMap = getImmutableMap();
try {
hashMap.put("Test", 23);
}
catch (UnsupportedOperationException e) {
System.out.println("ERROR: Unable to put new value.");
}
try {
hashMap.clear();
}
catch (UnsupportedOperationException e) {
System.out.println("ERROR: Unable to clear map.");
}
try {
hashMap.putIfAbsent("Test", 23);
}
catch (UnsupportedOperationException e) {
System.out.println("ERROR: Unable to put if absent.");
}
for ( String key : hashMap.keySet() ) {
System.out.printf("key = %s, value = %s%n", key, hashMap.get(key));
}
}
private static Map<String,Integer> getImmutableMap() {
Map<String,Integer> hashMap = new HashMap<>();
hashMap.put("Key 1", 34);
hashMap.put("Key 2", 105);
hashMap.put("Key 3", 144);
 
return Collections.unmodifiableMap(hashMap);
}
}
</syntaxhighlight>
 
{out}}
<pre>
ERROR: Unable to put new value.
ERROR: Unable to clear map.
ERROR: Unable to put if absent.
key = Key 1, value = 34
key = Key 2, value = 105
key = Key 3, value = 144
</pre>
 
=={{header|JavaScript}}==
Line 75 ⟶ 364:
{{works with|JavaScript|1.7}}
 
<langsyntaxhighlight lang="javascript">var keyError = new Error("Invalid Key Error (FixedKeyDict)") ;
 
function FixedKeyDict(obj)
Line 137 ⟶ 426:
return "FixedKeyDict{" + s + "}" ;
} ;
}</langsyntaxhighlight>
 
Test run:
 
<langsyntaxhighlight lang="javascript">
const BR = "<BR>\n"
 
Line 175 ⟶ 464:
pl("error test : " + e.message) ;
}
</syntaxhighlight>
</lang>
 
output :
Line 197 ⟶ 486:
</pre>
 
=={{header|Mathematicajq}}==
jq objects are JSON objects and can be created using JSON syntax, e.g. <syntaxhighlight lang="jq">{"language": "jq"}</syntaxhighlight>
<lang Mathematica>a[1] = "Do not modify after creation";
Objects can also be created programmatically, e.g. <syntaxhighlight lang="jq">{"one": 1} + {"two": 2}</syntaxhighlight>
 
jq objects, however, are really just values: they are immutable, and cannot be "deleted" any more than the number 1 can be deleted.
 
=={{header|Julia}}==
<syntaxhighlight lang="julia">
using BackedUpImmutable
 
function testBackedUpImmutableDict()
fibr = BackedUpImmutableDict{String,Int64}(["a" => 0, "b" => 1, "c" => 1, "d" => 2,
"e" => 3, "f" => 5, "g" => 8, "h" => 13, "i" => 21, "j" => 34, "extra" => -1])
 
x = fibr["extra"]
@test x == -1
fibr["extra"] = 0
y = fibr["extra"]
@test y == 0
restore!(fibr, "extra")
z = fibr["extra"]
@test z == -1
@test_throws String begin fibr["k"] = 55 end
fibr["a"] = 9
fibr["b"] = 7
# test restore all to default
restoreall!(fibr)
@test fibr["a"] == 0
end
</syntaxhighlight>
All tests pass.
 
=={{header|Kotlin}}==
<syntaxhighlight lang="scala">// version 1.1.2
 
fun main(args: Array<String>) {
// This line creates a read-only map which cannot be changed in any way nor cleared
val map = mapOf('A' to 65, 'B' to 66, 'C' to 67)
println(map)
}</syntaxhighlight>
 
{{out}}
<pre>
{A=65, B=66, C=67}
</pre>
 
=={{header|M2000 Interpreter}}==
{{trans|C sharp}}
<syntaxhighlight lang="m2000 interpreter">
Module CheckIt {
Class LockedHash {
Private:
inventory Vars ' no same keys
unlock
module nosuchvariable {
Error "No such value:"+letter$
}
module NoNewItem {
Error "No new item, use unlock method before"
}
module NoRemoveItem {
Error "Can't remove item, use unlock method before"
}
Public:
module Unlock {
.unlock<=True
}
module Writeln {
m=each(.Vars)
while m {
Print Quote$(Eval$(m, m^));",";Eval(m),
}
Print
}
Value (st$){
st$=Ucase$(st$)
if exist(.Vars, st$) then =Eval(.Vars) : Exit
.nosuchvariable st$
}
Set (st$){
st$=Ucase$(st$)
Read val
if exist(.Vars, st$) then Return .Vars, st$:=val : Exit
If .unlock then { Append .Vars, st$:=val} Else .NoNewItem
}
module Remove (st$) {
if not .unlock then .NoRemoveItem
st$=Ucase$(st$)
Try {
delete .Vars, st$
}
}
module Clear {
Clear .Vars
}
Class: ' this part exist only at construction
module LockedHash {
While match("SN") {
read st$, val
st$=ucase$(st$)
\\ if we append key which exist we get error
Append .Vars, st$:=val
}
}
}
d=LockedHash("a", 1, "b", 2)
d.writeln
d("a")=55 : d("b")=66
d.writeln
d.clear
d.writeln
d.unlock
d("a")=55 : d("b")=66
Print d("a")=55, d("a")/d("b")<1
d.remove "a"
d.writeln
}
Checkit
</syntaxhighlight>
 
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">a[1] = "Do not modify after creation";
a[2] = "Native demonstration";
Protect[a];</langsyntaxhighlight>
Example usage:
<pre>a[3] = 2
->Set::write: Tag a in a[1] is Protected. >>
</pre>
 
=={{header|Nim}}==
We leverage native stdlib table as our own object by implementing limited actual native table functionalities.
<syntaxhighlight lang="nim">import tables, options
 
type
MyTable = object
table: TableRef[string, int]
 
# return empty if the key is not available
proc `[]`(m: MyTable, key: string): Option[int] =
if key in m.table: result = some m.table[key]
else: result = none int
 
# update an item, doing nothing if the key is available during first initialization
proc `[]=`(m: var MyTable, key: string, val: int) =
if key notin m.table: return
m.table[key] = val
 
proc reset(m: var MyTable) =
for _, v in m.table.mpairs: v = 0
 
# sugar for defining MyTable object
proc toTable(vals: openarray[(string, int)]): MyTable =
result.table = newTable vals
 
proc main =
# MyTable construction
var myobj = {"key1": 1, "key2": 2, "key3": 3}.toTable
# test getting existing key
let val1 = myobj["key1"]
if val1.isSome: echo "val1: ", val1.get
 
# test adding new key
myobj["key4"] = 4
let val4 = myobj["key4"]
if val4.isSome: echo val4.get
else: echo "val4 is empty"
 
# test reset and test whether its value is zero-ed
reset myobj
doAssert myobj["key3"].get == 0
 
main()
</syntaxhighlight>
{{output}}
<pre>
val1: 1
val4 is empty</pre>
 
=={{header|Perl}}==
<syntaxhighlight lang="perl">use strict;
<lang perl>package LockedHash;
 
use parent Tie::Hash;
package LockedHash;
use parent 'Tie::Hash';
use Carp;
use strict;
 
sub TIEHASH {
Line 250 ⟶ 713:
}
 
sub lock_hash :prototype(\%) {
my $ref = shift;
tie(%$ref, __PACKAGE__, %$ref);
Line 275 ⟶ 738:
# add a new key x: will die
eval { $h{x} = 1 };
if ($@) { print "Operation error: $@" }</langsyntaxhighlight>output:<syntaxhighlight lang="text">a => 3
b => 4
c => 5
Line 284 ⟶ 747:
operation error: Can't add key x at test.pl line 14
LockedHash::STORE('LockedHash=HASH(0x8cebe14)', 'x', 1) called at test.pl line 66
eval {...} called at test.pl line 66</langsyntaxhighlight>
=={{header|Perl 6}}==
{{works with|rakudo|2013-02-22}}
Here we use delegation to handle all the normal hash methods that we don't need to override to define our new class.
<lang perl6>class FixedHash {
has $.hash handles *;
method new(*@args) { self.bless: *, hash => Hash.new: @args }
method at_key(FixedHash:D: $key is copy) is rw {
$!hash.exists($key) ?? $!hash.at_key($key) !! Nil;
}
method delete($key) { $!hash.{$key} = Nil }
}
 
=={{header|Phix}}==
# Testing
There is no native "read-only" setting on phix dictionaries, so the following wraps a pair of them to
my $fh = FixedHash.new: "a" => 1, "b" => 2;
provide the requested functionality.
say $fh<a b>; # 1 2
<!--<syntaxhighlight lang="phix">(phixonline)-->
$fh<b>:delete;
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
say $fh<a b>; # 1 Nil
<span style="color: #008080;">enum</span> <span style="color: #000000;">STD</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">CUR</span>
$fh<b> = 42;
<span style="color: #004080;">sequence</span> <span style="color: #000000;">fkds</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span> <span style="color: #000080;font-style:italic;">-- fixed key dictionaries ;-)</span>
say $fh<a b>; # 1 42
<span style="color: #004080;">integer</span> <span style="color: #000000;">freelist</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
say $fh<c>; # Nil
$fh<c> = 43; # error</lang>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">fkd_destroy</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">id</span><span style="color: #0000FF;">)</span>
{{out}}
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">std</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">]</span>
<pre>1 2
<span style="color: #7060A8;">destroy_dict</span><span style="color: #0000FF;">(</span><span style="color: #000000;">std</span><span style="color: #0000FF;">)</span>
1 Nil
<span style="color: #7060A8;">destroy_dict</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">)</span>
1 42
<span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">freelist</span>
Nil
<span style="color: #000000;">freelist</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">id</span>
Cannot assign to a non-container
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
in block at freezehash:21</pre>
<span style="color: #008080;">function</span> <span style="color: #000000;">fkd_new</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">key_pairs</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">std</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key_pairs</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">cur</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">(</span><span style="color: #000000;">std</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">id</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">freelist</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">id</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">fkds</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fkds</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">std</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">})</span>
<span style="color: #000000;">id</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fkds</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">freelist</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">std</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">id</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">fkd_clear</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">id</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">std</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">]</span>
<span style="color: #7060A8;">destroy_dict</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CUR</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">(</span><span style="color: #000000;">std</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">fkd_get</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">id</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">object</span> <span style="color: #000000;">key</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span><span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CUR</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">fkd_set</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">id</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">object</span> <span style="color: #000000;">key</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">data</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">node</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">getd_index</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span><span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CUR</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">node</span><span style="color: #0000FF;">=</span><span style="color: #004600;">NULL</span> <span style="color: #008080;">then</span> <span style="color: #008080;">throw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"invalid/new key"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #7060A8;">setd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span><span style="color: #000000;">data</span><span style="color: #0000FF;">,</span><span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CUR</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">fkd_remove</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">id</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">object</span> <span style="color: #000000;">key</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">std</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">],</span>
<span style="color: #000000;">node</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">getd_index</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span><span style="color: #000000;">std</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">node</span><span style="color: #0000FF;">=</span><span style="color: #004600;">NULL</span> <span style="color: #008080;">then</span> <span style="color: #008080;">throw</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"invalid key"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #7060A8;">setd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">getd_by_index</span><span style="color: #0000FF;">(</span><span style="color: #000000;">node</span><span style="color: #0000FF;">,</span><span style="color: #000000;">std</span><span style="color: #0000FF;">),</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">fkd_sprint</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">id</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">cur</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">fkds</span><span style="color: #0000FF;">[</span><span style="color: #000000;">id</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CUR</span><span style="color: #0000FF;">]</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">getd_all_keys</span><span style="color: #0000FF;">(</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">object</span> <span style="color: #000000;">ri</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">ri</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ri</span><span style="color: #0000FF;">,</span><span style="color: #000000;">cur</span><span style="color: #0000FF;">)}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">main</span><span style="color: #0000FF;">()</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">id</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">fkd_new</span><span style="color: #0000FF;">({{</span><span style="color: #008000;">"a"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #008000;">"b"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">}})</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">fkd_sprint</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- {{"a",1},{"b",2}}</span>
<span style="color: #000000;">fkd_set</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"a"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">55</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">fkd_set</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"b"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">66</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">fkd_sprint</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- {{"a",55},{"b",66}}</span>
<span style="color: #000000;">fkd_clear</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">fkd_sprint</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- {{"a",1},{"b",2}}</span>
<span style="color: #000000;">fkd_set</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"a"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">55</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">fkd_set</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"b"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">66</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">fkd_get</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"a"</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- 55</span>
<span style="color: #000000;">fkd_remove</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"a"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">try</span>
<span style="color: #000000;">fkd_set</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"NoNewKey"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">77</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">catch</span> <span style="color: #000000;">e</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">e</span><span style="color: #0000FF;">[</span><span style="color: #004600;">E_USER</span><span style="color: #0000FF;">]</span> <span style="color: #000080;font-style:italic;">-- "invalid/new key"</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">try</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">fkd_sprint</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- {{"a",1},{"b",66}}</span>
<span style="color: #000000;">fkd_destroy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">id</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #000000;">main</span><span style="color: #0000FF;">()</span>
<!--</syntaxhighlight>-->
 
=={{header|Python}}==
<langsyntaxhighlight lang="python">
from collections import UserDict
import copy
Line 402 ⟶ 924:
raise KeyError
else:
return super().setdefault(key, default)</langsyntaxhighlight>
 
=={{header|Racket}}==
Line 409 ⟶ 931:
 
Implementation of functions that handle fenced-hash:
<syntaxhighlight lang="racket">
<lang Racket>
;(struct fenced-hash (actual original) ...)
 
Line 447 ⟶ 969:
;private custom-write ;mode is ignored
(write-string "#fenced-hash" port)
(write (hash->list (fenced-hash-actual dict)) port))</langsyntaxhighlight>
 
Definition of the actual structure and a “public” creator:
<langsyntaxhighlight Racketlang="racket">(struct fenced-hash (actual original)
#:extra-constructor-name *fenced-hash ;private constructor
#:omit-define-syntaxes ;not sure this is a good idea
Line 470 ⟶ 992:
(define (fenced-hash . args) ; public constructor
(define original (apply hash args))
(*fenced-hash (hash-copy original) original))</langsyntaxhighlight>
 
'''Example:''' Use the fenced-hash functions:
<langsyntaxhighlight Racketlang="racket">(define d (fenced-hash "a" 1 "b" 2))
 
(displayln d)
Line 485 ⟶ 1,007:
(displayln d)
(fenced-hash-remove! d "a")
(displayln d)</langsyntaxhighlight>
{{out}}
<pre>#fenced-hash(("b" . 2) ("a" . 1))
Line 494 ⟶ 1,016:
 
'''Example (continued):''' Use the same object as a dict. The dict-clear! method is not defined, so we must call fenced-hash-clear! instead.
<langsyntaxhighlight Racketlang="racket">(fenced-hash-clear! d)
(displayln d)
(dict-set! d "a" 55)
Line 505 ⟶ 1,027:
(displayln d)
(dict-remove! d "a")
(displayln d)</langsyntaxhighlight>
{{out}}
<pre>#fenced-hash(("b" . 2) ("a" . 1))
Line 513 ⟶ 1,035:
#fenced-hash(("b" . 66) ("a" . 1))</pre>
 
=={{header|RubyRaku}}==
(formerly Perl 6)
{{in progress|lang=Ruby|day=15|month=February|year=2011}}
{{Works with|rakudo|2016.08}}
Here we use delegation to handle all the normal hash methods that we don't need to override to define our new class.
<syntaxhighlight lang="raku" line>class FixedHash {
has $.hash handles *;
method new(*@args) { self.bless: hash => Hash.new: @args }
method AT-KEY(FixedHash:D: $key is copy) is rw {
$!hash.EXISTS-KEY($key) ?? $!hash.AT-KEY($key) !! Failure.new(q{can't store value for unknown key});
}
method DELETE-KEY($key) { $!hash.{$key} = Nil }
}
 
# Testing
TODO: Write comments for FencedHash::new, FencedHash#delete and related methods. Add more methods (merge, merge!, reject, reject!, select, select!, update). Explain why FencedHash#replace and FencedHash#shift will not exist.
my $fh = FixedHash.new: "a" => 1, "b" => 2;
say $fh<a b>; # 1 2
$fh<b>:delete;
say $fh<a b>; # 1 Nil
$fh<b> = 42;
say $fh<a b>; # 1 42
say $fh<c>; # Nil
$fh<c> = 43; # error
</syntaxhighlight>
{{out}}
<pre>(1 2)
(1 (Any))
(1 42)
can't store value for unknown key
in block <unit> at native-demonstration.p6:17
 
Actually thrown at:
<lang ruby># fencedhash.rb
in block <unit> at native-demonstration.p6:17</pre>
require 'forwardable'
 
By defining [http://design.raku.org/S12.html#FALLBACK_methods FALLBACK] any class can handle undefined method calls. Since any class inherits plenty of methods from <tt>Any</tt> our magic object will be more of a novice conjurer then a master wizard proper.
# A FencedHash acts like a Hash, but with a fence around its keys.
# After the creation of a FencedHash, one cannot add nor remove keys.
# Any attempt to insert a new key will raise KeyError. Any attempt to
# delete a key-value pair will keep the key but will reset the value to
# the default value.
class FencedHash < Object
extend Forwardable
include Enumerable
 
<syntaxhighlight lang="raku" line>class Magic {
#--
has %.hash;
# @hash: our Hash inside the fence
multi method FALLBACK($name, |c) is rw { # this will eat any extra parameters
# @default_proc: passes self, not @hash
%.hash{$name}
#++
}
def_delegators(:@hash, :[], :assoc,
:compare_by_identity, :compare_by_identity?,
:default, :empty?, :fetch, :flatten,
:has_key?, :has_value?, :hash, :include?,
:key, :key?, :keys, :length, :member?,
:rassoc, :size, :to_a,
:values, :values_at, :value?)
attr_reader :default_proc
 
multi method FALLBACK($name) is rw {
# Acts like Hash::[] but creates a FencedHash.
%.hash{$name}
def self.[](*args)
}
allocate.instance_eval do
}
@hash = Hash[*args]
self
end
end
 
my $magic = Magic.new;
# call-seq:
$magic.foo = 10;
# FencedHash.new(obj=nil [,keys]) -> fh
say $magic.foo;
# FencedHash.new([keys]) { |fh, key| block } -> fh
$magic.defined = False; # error</syntaxhighlight>
#
# Creates a FencedHash.....
def initialize(*args, &block)
n = args.length
 
{{output}}
if block_given?
<pre>10
raise ArgumentError, "wrong number of arguments" if n > 1
Cannot modify an immutable Bool
in block <unit> at native-demonstration.p6:15</pre>
 
=={{header|Ring}}==
@default_proc = block
<syntaxhighlight lang="ring">
@hash = Hash.new { |hash, key| block[self, key] }
# Project : Create an object/Native demonstration
if n > 0
args[0].each { |key| @hash[key] = nil }
clear
end
else
raise ArgumentError, "wrong number of arguments" if n > 2
 
map = []
default = if n > 0 then n[0] else nil end
map["A"] = 65
@hash = Hash.new(default)
map["B"] = 66
if n > 1
map["C"] = 67
args[1].each { |key| @hash[key] = default }
see map + nl
end
</syntaxhighlight>
Output:
<pre>
A
65
B
66
C
67
</pre>
 
=={{header|Ruby}}==
{{works with|Ruby|1.9}}
<syntaxhighlight lang="ruby"># A FencedHash acts like a Hash, but with a fence around its keys.
# One may change its values, but not its keys. Any attempt to insert
# a new key raises KeyError. One may delete a key, but this only
# restores its original value.
#
# FencedHash reimplements these Hash methods: #[] #[]= #clear #delete
# #delete_if #default #default= #each_key #each_pair #each_value
# #fetch #has_key? #keep_if #keys #length #values #values_at
class FencedHash
 
# call-seq:
# FencedHash.new(hash, obj=nil) -> fh
#
# Creates a FencedHash that takes its keys and original values from
# a source _hash_. The source _hash_ can be any object that
# responds to each_pair. Sets the default value for missing keys to
# _obj_, so FencedHash#[] returns _obj_ when a key is not in fence.
def initialize(hash, obj=nil)
@default = obj
@hash = {}
hash.each_pair do |key, value|
# @hash[key][0] = current value
# @hash[key][1] = original value
@hash[key] = [value, value]
end
end
 
def initialize_copyinitialize_clone(orig)
# Object#clone calls here in Ruby 2.0. If _orig_ was frozen, then
# each array of _values_ is frozen, so make frozen clones.
super
@hashcopy = @hash.dup{}
@hash.each_pair {|key, values| copy[key] = values.clone }
@hash = copy
end
 
def initialize_dup(orig)
# Clears all values. For each key-value pair, this retains the key
# Object#dup calls here in Ruby 2.0. If _orig_ was frozen, then
# but resets the value to default.
# make duplicates that are not frozen.
#--
super
# The line "@hash = @hash" checks that _self_ is not frozen, because
copy = {}
# Object#freeze only freezes _self_ and not @hash.
@hash.each_pair {|key, values| copy[key] = values.dup }
#++
@hash = copy
def clear
@hash = @hash
@hash.each_key { |key| delete key }
self
end
 
# Retrieves current value for _key_, like Hash#[]. If _key_ is not
# .....
def# in fence, returns default=(obj) object.
def [](key)
@default_proc = nil
@hash.defaultvalues = obj@hash[key]
if values
values[0]
else
@default
end
end
 
# .....call-seq:
# fh[key] = value -> value
def default_proc=(proc_obj)
# fh.store(key, value) -> value
# Convert _proc_obj_ to a block parameter.
proc_obj = proc &proc_obj
 
@hash.default_proc = proc { |hash, key| proc_obj[self, key] }
@default_proc = proc_obj
end
 
# Deletes the value of the key-value pair for _key_.
#
# Sets _value_ for a _key_. Returns _value. If _key_ is innot the fence.....in
# fence, raises KeyError.
def delete(key)
def []=(key, value)
@hash = @hash
values = @hash[key]
 
beginif values
original_valuevalues[0] = @hash.fetch(key)value
rescue IndexError
# _key_ is not in the fence.
if block_given?
yield key
else
nil
end
else
raise KeyError, "fence prevents adding new key: #{key.inspect}"
# _key_ is in the fence.
if @default_proc
@default_proc[self, key]
else
@hash[key] = @hash.default
end
original_value
end
end
alias store []=
 
# Resets all keys to their original values. Returns self.
# .....
def delete_ifclear
@hash.each_value {|values| values[0] = values[1]}
return enum_for(:delete_if) unless block_given?
 
@hash = @hash
@hash.each { |key, value| delete key if yield key, value }
self
end
 
# Resets _key_ to its original value. Returns old value before
# Yields each key-value pair to the block, or returns an enumerator.
# reset. If _key_ is not in fence, returns +nil+.
# Acts like Hash#each.
def each &block # :yields: delete(key, value)
values = @hash[key]
return enum_for(:each) unless block
@hash.eachif &blockvalues
old = values[0]
values[0] = values[1]
old # return old
end # else return nil
end
alias each_pair each
 
# call-seq:
# Yields each key to the block, or returns an enumerator.
# fh.delete_if {|key, value| block } -> fh
# Acts like Hash#each_key.
# fh.delete_if -> enumerator
def each_key &block # :yields: key
#
return enum_for(:each_key) unless block
# Yields each _key_ with current _value_ to _block_. Resets _key_
@hash.each_key &block
# to its original value when block evaluates to true.
def delete_if
if block_given?
@hash.each_pair do |key, values|
yield(key, values[0]) and values[0] = values[1]
end
self
else
enum_for(:delete_if) { @hash.size }
end
end
 
# YieldsThe eachdefault value tofor thekeys block,not orin returns an enumeratorfence.
attr_accessor :default
# Acts like Hash#each_value.
def each_value &block # :yields: value
return enum_for(:each_value) unless block
@hash.each_value &block
end
 
# call-seq:
# Returns true if _other_ is a FencedHash and has the same key-value
# fh.each_key {|key| block} -> fh
# pairs as _self_. Acts like Hash#eql?.
# fh.each_key -> enumerator
#--
#
# Consistent with FencedHash#hash because it delegates to @hash.hash.
# Yields each key in fence to the block.
#++
def eql?each_key(other&block)
if block
FencedHash === other and
@hash.eql?each_key(other.instance_eval { @hash }&block)
self
else
enum_for(:each_key) { @hash.size }
end
end
 
# call-seq:
# Returns true if _other_ is a FencedHash and if the key-value pairs
# fh.each_pair {|key, value| block} -> fh
# of _self_ equal those of _other_. Acts like Hash#==.
# fh.each_pair -> enumerator
def ==(other)
#
FencedHash === other and
# Yields each key-value pair to the block, like Hash#each_pair.
@hash == (other.instance_eval { @hash })
# This yields each [key, value] as an array of 2 elements.
def each_pair
if block_given?
@hash.each_pair {|key, values| yield [key, values[0]] }
self
else
enum_for(:each_pair) { @hash.size }
end
end
 
# .....call-seq
# fh.each_value {|value| block} -> fh
def keep_if
# fh.each_value -> enumerator
return enum_for(:keep_if) unless block_given?
#
 
# Yields current value of each key-value pair to the block.
@hash = @hash
def each_value
@hash.each { |key, value| delete key unless yield key, value }
selfif block_given?
@hash.each_value {|values| yield values[0] }
end
 
# Stores a _value_ for a _key_. This only works if _key_ is in the
# fence; FencedHash prevents the insertion of new keys. If _key_ is
# not in the fence, then this method raises KeyError.
def store(key, value)
@hash = @hash
if @hash.has_key? key
@hash.store(key, value)
else
enum_for(:each_value) { @hash.size }
c = if defined? KeyError then KeyError else IndexError end
raise c, "fence prevents new key: #{key}"
end
end
alias []= store
 
# call-seq:
# Converts _self_ to a regular Hash. Returns a new Hash that has the
# fenhsh.fetch(key [,default])
# same key-value pairs as _self_.
# fenhsh.fetch(key) {|key| block }
def to_hash
#
@hash.dup
# Fetches value for _key_. Takes same arguments as Hash#fetch.
end
def fetch(*argv)
argc = argv.length
unless argc.between?(1, 2)
raise(ArgumentError,
"wrong number of arguments (#{argc} for 1..2)")
end
if argc == 2 and block_given?
warn("#{caller[0]}: warning: " +
"block supersedes default value argument")
end
 
key, default = argv
# Converts _self_ to a String.
values = @hash[key]
def to_s
if values
"#<#{self.class}: #{@hash.inspect}>"
values[0]
elsif block_given?
yield key
elsif argc == 2
default
else
raise KeyError, "key not found: #{key.inspect}"
end
end
alias inspect to_s
end</lang>
 
# Freezes this FencedHash.
<lang ruby># fh-test.rb
def freeze
require 'fencedhash'
@hash.each_value {|values| values.freeze }
require 'test/unit'
super
 
class TestFencedHash < Test::Unit::TestCase
if RUBY_VERSION >= "1.9"
KeyEx = KeyError
FrozenEx = RuntimeError
else
KeyEx = IndexError
FrozenEx = TypeError
end
 
# Returns true if _key_ is in fence.
def setup
def has_key?(key)
@fh = FencedHash[:q => 11, :w => 22, :e => 33,
@hash.has_key?(key)
:r => 44, :t => 55, :y => 66]
end
alias include? has_key?
alias member? has_key?
 
# call-seq:
def test_bracket_operator
# fh.keep_if {|key, value| block } -> fh
assert_equal 11, @fh[:q]
# fh.keep_if -> enumerator
assert_equal 22, @fh[:w]
#
assert_equal 33, @fh[:e]
# Yields each _key_ with current _value_ to _block_. Resets _key_
assert_equal 44, @fh[:r]
# to its original value when block evaluates to false.
assert_equal 55, @fh[:t]
def keep_if
assert_equal 66, @fh[:y]
if block_given?
assert_nil @fh[:u]
@hash.each_pair do |key, values|
yield(key, values[0]) or values[0] = values[1]
end
self
else
enum_for(:keep_if) { @hash.size }
end
end
 
# Returns array of keys in fence.
def test_delete
def keys
assert_equal 44, (@fh.delete :r)
@hash.keys
assert_nil @fh.fetch(:r)
assert_nil @fh.delete(:r)
assert_nil @fh.delete(:u)
@fh[:r] = "replacement"
assert_equal "replacement", (@fh.delete :r)
end
 
# Returns number of key-value pairs.
def test_delete_if
def length
a = @fh.delete_if { |key, value| key == :t || value == 66 }
@hash.length
assert_same @fh, a
assert_equal 2, @fh.values.grep(nil).length
@fh[:y] = "why?"
@fh[:t] = "tea!"
assert_equal 0, @fh.values.grep(nil).length
end
alias size length
 
# Converts self to a regular Hash.
def test_default
def to_h
fruit = FencedHash.new(0, [:apple, :banana, :cranberry])
result = Hash.new(@default)
assert_equal [0, 0, 0], fruit.values
@hash.each_pair {|key, values| result[key] = values[0]}
fruit[:apple] += 1
result
fruit[:banana] += 5
fruit[:cranberry] *= 5
assert_equal 1, fruit[:apple]
assert_equal 5, fruit[:banana]
assert_equal 0, fruit[:cranberry]
assert_equal 0, fruit.default
end
 
# Converts self to a String.
def test_default_assign
def to_s
assert_nil @fh.default
"#<#{self.class}: #{to_h}>"
@fh.delete :w
 
@fh.default = -1
assert_equal -1, @fh.default
@fh.delete :e
 
assert_nil @fh[:w]
assert_equal -1, @fh[:e]
end
alias inspect to_s
 
# Returns array of current values.
def test_default_proc
def values
count = 0
@hash.each_value.map {|values| values[0]}
fruit = FencedHash.new([:apple, :banana, :cranberry]) do |h, k|
if h.key? k then h[k] = [] else count += 1 end
end
fruit[:apple].push :red
fruit[:banana].concat [:green, :yellow]
fruit[:cranberry].push :red
assert_equal 1, fruit[:orange]
assert_equal [:red], fruit[:apple]
assert_equal [:green, :yellow], fruit[:banana]
assert_equal [:red], fruit.delete(:cranberry)
assert_equal 2, fruit[:orange]
assert_equal [], fruit[:cranberry]
assert_nil fruit.delete(:orange)
assert_equal 3, fruit[:orange]
assert_equal [], fruit.default_proc[FencedHash[1 => 2], 1]
end
 
# Returns array of current values for keys, like Hash#values_at.
def test_each
def values_at(*keys)
count = 0
@fhkeys.each domap {|key, value| self[key]}
assert_kind_of Symbol, key
assert_kind_of Integer, value
assert_equal true, (@fh.has_key? key)
assert_equal true, (@fh.has_value? value)
count += 1
end
assert_equal 6, count
end
end</syntaxhighlight>
 
=={{header|Scala}}==
def test_eql?
{{Out}}Best seen running in your browser either by [https://scalafiddle.io/sf/OuVZ3bT/0 ScalaFiddle (ES aka JavaScript, non JVM)] or [https://scastie.scala-lang.org/qW5qzmdKSZSyAbZEqDROoA Scastie (remote JVM)].
other = FencedHash[:r, 44, :t, 55, :y, 66,
<syntaxhighlight lang="scala">object CreateMapObject extends App {
:q, 11, :w, 22, :e, 33]
val floatmap = FencedHash[:yMap('A' -> 65, 'B' -> 66.0, :t,'C' 55.0,-> :r, 44.0,67)
:e, 33.0, :w, 22.0, :q, 11.0]
tt = [true, true]
ff = [false, false]
 
println(map)
if RUBY_VERSION >= "1.9"
}</syntaxhighlight>
assert_equal tt, [(@fh.eql? other), (other.eql? @fh)]
assert_equal ff, [(@fh.eql? float), (float.eql? @fh)]
assert_equal ff, [(other.eql? float), (float.eql? other)]
end
 
=={{header|Tcl}}==
assert_equal tt, [@fh == other, other == @fh]
This solution uses a dict(ionary), so requires Tcl 8.5 or better. Variable traces are used to detect write or unset access to such a protected variable, restore it to the backup value at protection time, and throw an exception
assert_equal tt, [@fh == float, float == @fh]
assert_equal tt, [other == float, float == other]
 
<syntaxhighlight lang="tcl">proc protect _var {
h = @fh.to_hash
ifupvar RUBY_VERSION1 >=$_var "1.9"var
trace add variable var {write unset} [list protect0 $var]
assert_equal ff, [(@fh.eql? h), (h.eql? @fh)]
}
end
proc protect0 {backup name1 name2 op} {
assert_equal ff, [@fh == h, h == @fh]
upvar 1 $name1 var
end
trace remove variable var {write unset} [list protect 0 $backup]
set var $backup
trace add variable var {write unset} [list protect0 $backup]
return -code error "$name1 is protected"
}
proc trying cmd { #-- convenience function for demo
puts "trying: $cmd"
if [catch {uplevel 1 $cmd} msg] {puts $msg}
}</syntaxhighlight>
Testing:
dict set dic 1 one
dict set dic 2 two
puts dic:$dic
protect dic
trying "dict set dic 3 three"
puts dic:$dic
trying "dict unset dic 1"
trying "unset dic"
puts dic:$dic
 
displays on stdout:
def test_fetch
dic:1 one 2 two
assert_equal 11, @fh.fetch(:q)
trying: dict set dic 3 three
assert_equal 22, @fh.fetch(:w)
can't set "dic": dic is protected
assert_equal 33, @fh.fetch(:e)
dic:1 one 2 two
assert_equal 44, @fh.fetch(:r)
trying: dict unset dic 1
assert_equal 55, @fh.fetch(:t)
can't set "dic": dic is protected
assert_equal 66, @fh.fetch(:y)
trying: unset dic
assert_raises(KeyEx) { @fh.fetch :u }
dic:1 one 2 two
end
 
=={{header|Wren}}==
def test_freeze
<syntaxhighlight lang="wren">class FixedSizeMap {
assert_equal false, @fh.frozen?
construct new(map) {
@fh.freeze
// copy the map so it cannot be mutated from the original reference
_map = {}
for (me in map.toList) _map[me.key] = me.value
}
 
containsKey(key) { _map[key] != null }
2.times do
assert_equal true, @fh.frozen?
assert_raises(FrozenEx) { @fh.clear }
assert_raises(FrozenEx) { @fh.delete :q }
assert_raises(FrozenEx) { @fh.delete_if { true } }
assert_raises(FrozenEx) { @fh.keep_if { false } }
assert_raises(FrozenEx) { @fh.store :w, "different" }
assert_raises(FrozenEx) { @fh[:w] = "different" }
 
count { _map.count }
# Repeat the tests with a clone. The clone must be frozen.
@fh = @fh.clone
end
 
keys { _map.keys }
# A duplicate is not frozen.
@fh = @fh.dup
assert_equal false, @fh.frozen?
@fh[:w] = "different"
assert_equal "different", @fh[:w]
end
 
values { _map.values }
def test_has_key
2.times do |t|
assert_equal true, (@fh.has_key? :y)
assert_equal true, (@fh.include? :y)
assert_equal true, (@fh.key? :y)
assert_equal true, (@fh.member? :y)
 
[key] { _map[key] }
assert_equal false, (@fh.has_key? :u)
[key] =(value) {
assert_equal false, (@fh.include? :u)
// do nothing if key doesn't already exist
assert_equal false, (@fh.key? :u)
if (_map[key] != null) _map[key] = value
assert_equal false, (@fh.member? :u)
}
 
reset(key) {
# Repeat the tests.
# The fencevar mustt prevent= any changes to the keys_map[key].type
// leave unaltered if no suitable default value
@fh.delete :y
(@fh _map[:ukey] = "value"(t == Num) rescue "ok" ? 0 :
(t == String) ? "":
end
(t == Bool) ? false :
end
(t == List) ? [] :
(t == Map) ? {} : _map[key]
}
 
iterate(iterator) { _map.iterate(iterator) }
def test_has_value
iteratorValue(iterator) { _map.iteratorValue(iterator) }
assert_equal true, (@fh.has_value? 22)
assert_equal true, (@fh.value? 22)
 
toString { _map.toString }
assert_equal false, (@fh.has_value? 4444)
}
assert_equal false, (@fh.value? 4444)
end
 
var map = { "a": 1, "b": 2 }
def test_inject
var fsm = FixedSizeMap.new(map)
# To get an :inject method, FencedHash should mix in Enumerable.
System.print(fsm)
assert_kind_of Enumerable, @fh
System.print(fsm.count)
assert_equal 231, @fh.inject(0) { |sum, kv| sum + kv[1] }
fsm["a"] = 3
end
fsm["b"] = 4
System.print(fsm)
System.print(fsm.containsKey("c"))
fsm["c"] = 5 // attempt to add a new key/value pair
System.print(fsm) // ignored
fsm.reset("a")
System.print(fsm)
System.print(fsm.keys.toList)
System.print(fsm.values.toList)
for (me in fsm) System.print([me.key, me.value])
</syntaxhighlight>
 
{{out}}
def test_keep_if
<pre>
a = @fh.keep_if { |key, value| key == :t || value == 66 }
{b: 2, a: 1}
assert_same @fh, a
2
assert_equal 4, @fh.values.grep(nil).length
{b: 4, a: 3}
@fh.delete :y
false
@fh.delete :t
{b: 4, a: 3}
assert_equal 6, @fh.values.grep(nil).length
{b: 4, a: 0}
end
[b, a]
[4, 0]
[b, 4]
[a, 0]
</pre>
 
=={{header|zkl}}==
def test_keys
zkl has two dictionary objects: SD, a small dictionary that is created immutable and the "regular" dictionary has has a makeReadOnly method. They both behave the same when locked down.
assert_equal([:e, :q, :r, :t, :w, :y],
<syntaxhighlight lang="zkl">d:=SD("one",1,"two",2);
@fh.keys.sort_by { |o| o.to_s })
d.keys; //-->L("one","two")
end
d["one"]; //-->1
d.add("three",3); // error thrown
d.pop("one") // error thrown</syntaxhighlight>
 
def test_length
assert_equal 6, @fh.length
assert_equal 6, @fh.size
end
 
def test_store
assert_raises(KeyEx) { @fh[:a] = 111 }
assert_equal 222, (@fh[:e] = 222)
assert_equal 222, (@fh.fetch :e)
assert_equal 333, @fh.store(:e, 333)
assert_equal 333, @fh[:e]
end
 
def test_values
assert_equal [11, 22, 33, 44, 55, 66], @fh.values.sort!
end
 
if RUBY_VERSION >= "1.8.7"
def test_delete_if_enum
a = @fh.delete_if.with_index { |kv, i| i >= 2 }
assert_same @fh, a
assert_equal 4, @fh.values.grep(nil).length
end
 
def test_keep_if_enum
a = @fh.keep_if.with_index { |kv, i| i >= 2 }
assert_same @fh, a
assert_equal 2, @fh.values.grep(nil).length
end
end
 
if RUBY_VERSION >= "1.9"
def test_class_bracket_operator
from_pairs = FencedHash[10, "ten", 20, "twenty", 30, "thirty"]
from_alist = FencedHash[ [ [10, "ten"], [20, "twenty"], [30, "thirty"] ] ]
from_hash = FencedHash[10 => "ten", 20 => "twenty", 30 => "thirty"]
from_fhash = FencedHash[from_pairs]
 
[from_pairs, from_alist, from_hash, from_fhash, from_pairs
].each_cons(2) do |a, b|
assert_equal a, b
assert_not_same a, b
end
end
 
def test_default_proc_assign
assert_nil @fh.default_proc
p = @fh.default_proc = proc { |h, k| h[k] = :deleted }
assert_same p, @fh.default_proc
 
assert_equal 11, @fh.delete(:q)
assert_equal :deleted, @fh[:q]
assert_raises(KeyEx) { @fh[:u] }
 
@fh.default = :value
assert_nil @fh.default_proc
@fh.default_proc = p
assert_nil @fh.default
end
 
def test_each_rewind
class << @fh
attr_reader :test_rewind
def rewind
@test_rewind = "correct"
end
end
assert_nil @fh.test_rewind
 
# @fh.each.rewind must call @fh.rewind. If @fh forwards :each
# to another object then this test fails.
@fh.each.rewind
assert_equal "correct", @fh.test_rewind
end
 
def test_insertion_order
assert_equal [:q, :w, :e, :r, :t, :y], @fh.keys
assert_equal [11, 22, 33, 44, 55, 66], @fh.values
end
 
def test_key
assert_equal :q, @fh.key(11)
assert_equal :w, @fh.key(22)
assert_equal :e, @fh.key(33)
assert_equal :r, @fh.key(44)
assert_equal :t, @fh.key(55)
assert_equal :y, @fh.key(66)
assert_nil @fh.key(77)
end
end
end</lang>
 
{{omit from|AWK|Cannot tie the []= operator nor the delete command to custom functions.}}
Line 1,024 ⟶ 1,489:
{{omit from|C|No associative arrays.}}
{{omit from|dc|No associative arrays.}}
{{omit from|Go|No operator overriding.}}
{{omit from|GUISS}}
{{omit from|PureBasic}}
{{omit from|sed|No associative arrays.}}
{{omit from|Tcl|The value system and object system do not interact in a way that is necessary to support the prerequisites of this task. In particular, native dicts and arrays are not objects, and objects formally occupy part of the space of functions and not values or variables.}}
{{omit from|UNIX Shell|Bourne shell does not have these, but bashisms may be available on some shells}}
{{omit from|PicoLisp|Can't change the behavior of the property system without changing the language around it.}}
9,485

edits