Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit ff5caf0

Browse files
author
Badacadabra
committed
Add Interpreter (ES5 + ES6 + CoffeeScript + TypeScript)
1 parent 33929d2 commit ff5caf0

File tree

7 files changed

+412
-0
lines changed

7 files changed

+412
-0
lines changed
2.24 KB
Binary file not shown.
19.4 KB
Loading[フレーム]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Synopsis
2+
3+
I love music and I want to be able to analyze some compositions like "Moonlight Sonata" by Beethoven.
4+
5+
# Problem
6+
7+
Music is a beautiful but complex universe, and listening to music does not mean understanding it. To analyze compositions, music theory is an absolute necessity. Since music is a language, pretty much like English or French are, it has its own grammar. Understanding this grammar makes it possible to understand a composition. But here the question is: how do we describe a grammar with a programming language?
8+
9+
# Solution
10+
11+
The Interpreter pattern is exactly what we need to tell JavaScript how to interpret music grammar. Here we can have:
12+
13+
* A concrete context to analyze, which will be a sonata (e.g. "Moonlight Sonata")
14+
* An abstract representation of music notation (abstract class or interface)
15+
* Concrete (terminal and nonterminal) expressions like Note or Arpeggio
16+
17+
The distinction between terminal and nonterminal expressions in the Interpreter pattern is a bit like the distinction between simple and composed elements in the Composite pattern. A note is a terminal expression because it is an "elementary symbol" of the grammar. However, an arpeggio is a nonterminal expression because it can be considered as a group of notes.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# ==============================
2+
# CONCRETE CONTEXT
3+
# ==============================
4+
5+
class Sonata
6+
constructor: (@name, @composer) ->
7+
8+
getName: ->
9+
@_name
10+
11+
getComposer: ->
12+
@_composer
13+
14+
# ==============================
15+
# ABSTRACT EXPRESSION
16+
# ==============================
17+
18+
class MusicNotation
19+
constructor: (name) ->
20+
throw new Error "You cannot instantiate an abstract class!" if @constructor is MusicNotation
21+
@_name = name
22+
23+
interpret: (sonata) ->
24+
throw new Error "You cannot call an abstract method!"
25+
26+
# ==============================
27+
# CONCRETE EXPRESSIONS
28+
# ==============================
29+
30+
# Terminal expression
31+
class Note extends MusicNotation
32+
constructor: (name) ->
33+
super name
34+
35+
interpret: (sonata) ->
36+
"#{sonata.composer} played #{@_name}\n"
37+
38+
# Nonterminal expression
39+
class Arpeggio extends MusicNotation
40+
constructor: (name) ->
41+
super name
42+
@_notes = []
43+
44+
addNotes: (newNotes) ->
45+
@_notes.push note for note in newNotes
46+
47+
48+
interpret: (sonata) ->
49+
res = ""
50+
res += note.interpret sonata for note in @_notes
51+
res += "This was a #{@_name} arpeggio from #{sonata.name}.\n"
52+
res
53+
54+
# ==============================
55+
# CLIENT CODE
56+
# ==============================
57+
58+
# To avoid overcomplexity, we only take into account the first measure and the right hand (G-clef)
59+
sonata = new Sonata "Moonlight Sonata", "Beethoven"
60+
61+
# Notes
62+
gSharp = new Note "G#"
63+
cSharp = new Note "C#"
64+
e = new Note "E"
65+
66+
# Arpeggio
67+
cSharpMinor = new Arpeggio "C#m"
68+
cSharpMinor.addNotes [gSharp, cSharp, e]
69+
70+
# Melody
71+
melody = []
72+
73+
melody.push cSharpMinor for i in [1..4] # The same musical pattern is repeated four times...
74+
75+
# Interpretation
76+
console.log arpeggio.interpret sonata for arpeggio in melody
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// ==============================
2+
// CONCRETE CONTEXT
3+
// ==============================
4+
5+
class Sonata {
6+
private name: string;
7+
private composer: string;
8+
9+
constructor(name: string, composer: string) {
10+
this.name = name;
11+
this.composer = composer;
12+
}
13+
14+
public getName(): string {
15+
return this.name;
16+
}
17+
18+
public getComposer(): string {
19+
return this.composer;
20+
}
21+
}
22+
23+
// ==============================
24+
// ABSTRACT EXPRESSION
25+
// ==============================
26+
27+
abstract class MusicNotation {
28+
protected name: string;
29+
30+
constructor(name: string) {
31+
this.name = name;
32+
}
33+
34+
public abstract interpret(sonata: Sonata): string;
35+
}
36+
37+
// ==============================
38+
// CONCRETE EXPRESSIONS
39+
// ==============================
40+
41+
// Terminal expression
42+
class Note extends MusicNotation {
43+
constructor(name: string) {
44+
super(name);
45+
}
46+
47+
public interpret(sonata: Sonata): string {
48+
return `${sonata.getComposer()} played ${this.name}\n`;
49+
}
50+
}
51+
52+
// Nonterminal expression
53+
class Arpeggio extends MusicNotation {
54+
private notes: Note[] = [];
55+
56+
constructor(name: string) {
57+
super(name);
58+
}
59+
60+
public addNotes(newNotes: Note[]): void {
61+
for (let note of newNotes) {
62+
this.notes.push(note);
63+
}
64+
}
65+
66+
public interpret(sonata: Sonata): string {
67+
let res: string = "";
68+
for (let note of this.notes) {
69+
res += note.interpret(sonata);
70+
}
71+
res += `This was a ${this.name} arpeggio from ${sonata.getName()}.\n`;
72+
return res;
73+
}
74+
}
75+
76+
// ==============================
77+
// CLIENT CODE
78+
// ==============================
79+
80+
// To avoid overcomplexity, we only take into account the first measure and the right hand (G-clef)
81+
let sonata: Sonata = new Sonata("Moonlight Sonata", "Beethoven");
82+
83+
// Notes
84+
let gSharp: Note = new Note("G#"),
85+
cSharp: Note = new Note("C#"),
86+
e: Note = new Note("E");
87+
88+
// Arpeggio
89+
let cSharpMinor: Arpeggio = new Arpeggio("C#m");
90+
cSharpMinor.addNotes([gSharp, cSharp, e]);
91+
92+
// Melody
93+
let melody: Arpeggio[] = [];
94+
95+
for (let i = 0; i < 4; i++) { // The same musical pattern is repeated four times...
96+
melody.push(cSharpMinor);
97+
}
98+
99+
// Interpretation
100+
for (let arpeggio of melody) {
101+
console.log(arpeggio.interpret(sonata));
102+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
'use strict';
2+
3+
// ==============================
4+
// CONCRETE CONTEXT
5+
// ==============================
6+
7+
var Sonata = (function () {
8+
function Sonata(name, composer) {
9+
this._name = name;
10+
this._composer = composer;
11+
}
12+
13+
Sonata.prototype.getName = function () {
14+
return this._name;
15+
};
16+
17+
Sonata.prototype.getComposer = function () {
18+
return this._composer;
19+
};
20+
21+
return Sonata;
22+
})();
23+
24+
// ==============================
25+
// ABSTRACT EXPRESSION
26+
// ==============================
27+
28+
var MusicNotation = (function() {
29+
function MusicNotation(name) {
30+
if (this.constructor === MusicNotation) {
31+
throw new Error("You cannot instantiate an abstract class!");
32+
}
33+
this._name = name;
34+
}
35+
36+
MusicNotation.prototype.interpret = function (sonata) {
37+
throw new Error("You cannot call an abstract method!");
38+
};
39+
40+
return MusicNotation;
41+
})();
42+
43+
// ==============================
44+
// CONCRETE EXPRESSIONS
45+
// ==============================
46+
47+
// Terminal expression
48+
var Note = (function () {
49+
function Note(name) {
50+
MusicNotation.call(this, name);
51+
}
52+
Note.prototype = Object.create(MusicNotation.prototype);
53+
Note.prototype.constructor = Note;
54+
55+
Note.prototype.interpret = function (sonata) {
56+
return sonata.getComposer() + " played " + this._name + "\n";
57+
};
58+
59+
return Note;
60+
})();
61+
62+
// Nonterminal expression
63+
var Arpeggio = (function () {
64+
var notes = [];
65+
66+
function Arpeggio(name) {
67+
MusicNotation.call(this, name);
68+
}
69+
Arpeggio.prototype = Object.create(MusicNotation.prototype);
70+
Arpeggio.prototype.constructor = Arpeggio;
71+
72+
Arpeggio.prototype.addNotes = function (newNotes) {
73+
for (var i = 0, len = newNotes.length; i < len; i++) {
74+
notes.push(newNotes[i]);
75+
}
76+
};
77+
78+
Arpeggio.prototype.interpret = function (sonata) {
79+
var res = "";
80+
for (var i = 0, len = notes.length; i < len; i++) {
81+
res += notes[i].interpret(sonata);
82+
}
83+
res += "This was a " + this._name + " arpeggio from " + sonata.getName() + ".\n";
84+
return res;
85+
};
86+
87+
return Arpeggio;
88+
})();
89+
90+
// ==============================
91+
// CLIENT CODE
92+
// ==============================
93+
94+
// To avoid overcomplexity, we only take into account the first measure and the right hand (G-clef)
95+
var sonata = new Sonata("Moonlight Sonata", "Beethoven");
96+
97+
// Notes
98+
var gSharp = new Note("G#"),
99+
cSharp = new Note("C#"),
100+
e = new Note("E");
101+
102+
// Arpeggio
103+
var cSharpMinor = new Arpeggio("C#m");
104+
cSharpMinor.addNotes([gSharp, cSharp, e]);
105+
106+
// Melody
107+
var melody = [];
108+
109+
for (var i = 0; i < 4; i++) { // The same musical pattern is repeated four times...
110+
melody.push(cSharpMinor);
111+
}
112+
113+
// Interpretation
114+
for (var j = 0, len = melody.length; j < len; j++) {
115+
console.log(melody[j].interpret(sonata));
116+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /