SlideShare une entreprise Scribd logo
1  sur  168
Télécharger pour lire hors ligne
What Parnas72

Means for D
Luís Marques, DConf 2016, May 4th 2016

luis@luismarques.eu
Reproducing the Classics
• My experience growing
artificial societies
Growing Artificial Societies
• Sugarscape
Growing Artificial Societies
• Sugarscape
Growing Artificial Societies
• Sugarscape
SettingsSettings
World speed:
Agents:
World resources:
Minimum vision:
Maximum vision:
Resource types:
Start
Sex
Death Sickness
Sharing
WikipedianTrade
Metabolism:
Grow rate:
Spacing:
Agent resources:
Growing Artificial Societies
• Sugarscape
Animation B-3. WealthHistogramEvolutionunderRules({GI), {M,
R[60,IOO]}) from aRandomInitial Distributionof Agents
4
39 39
12 15 18 21 24 27 : I )
7
3
11 22 33 44 55 66 77 88 99 110
0 20 40 60 80 100 120 140 160 la : J 200
1:1)-.122
What Parnas72 Means for D
• Who’s Parnas?
• What’s Parnas72?
• Electrical engineer
• PhD student of Alan Perlis
• Applies traditional engineering
principles to software design
• Critic of SDI
• Known for introducing the
concept of “Information
hiding”
David Lorge Parnas
Parnas72
On the Criteria
To Be Used in
Decomposing
Systems into
Modules
Programming R. Morris
Techniques Editor
On the Criteria To Be
Used in Decomposing
Systems into Modules
D.L. Parnas
Carnegie-Mellon University
This paper discusses modularization as a mechanism
for improving the flexibility and comprehensibility of a
system while allowing the shortening of its development
time. The effectiveness of a "modularization" is
dependent upon the criteria used in dividing the system
into modules. A system design problem is presented and
both a conventional and unconventional decomposition
are described. It is shown that the unconventional
decompositions have distinct advantages for the goals
outlined. The criteria used in arriving at the decom-
positions are discussed. The unconventional decomposi-
tion, if implemented with the conventional assumption
that a module consists of one or more subroutines, will
be less efficient in most cases. An alternative approach
to implementation which does not have this effect is
sketched.
Key Words and Phrases: software, modules,
modularity, software engineering, KWIC index,
software design
CR Categories: 4.0
Introduction
A lucid statement of the philosophy of modular
programming can be found in a 1970 textbook on the
design of system programs by Gouthier and Pont [1,
¶I0.23], which we quote below: 1
A well-defined segmentation of the project effort ensures
system modularity. Each task forms a separate, distinct program
module. At implementation time each module and its inputs and
outputs are well-defined, there is no confusion in the intended
interface with other system modules. At checkout time the in-
tegrity of the module is tested independently; there are few sche-
duling problems in synchronizing the completion of several tasks
before checkout can begin. Finally, the system is maintained in
modular fashion; system errors and deficiencies can be traced to
specific system modules, thus limiting the scope of detailed error
searching.
Usually nothing is said about the criteria to be used
in dividing the system into modules. This paper will
discuss that issue and, by means of examples, suggest
some criteria which can be used in decomposing a
system into modules.
Copyright @ 1972,Association for Computing Machinery, Inc.
General permission to republish, but not for profit, all or part
of this material is granted, provided that reference is made to this
publication, to its date of issue, and to the fact that reprinting
privileges were granted by permission of the Association for Com-
puting Machinery.
Author's address: Department of Computer Science, Carnegie-
Mellon University, Pittsburgh, PA 15213.
1053
A Brief Status Report
The major advancement in the area of modular
programming has been the development of coding
techniques and assemblers which (l) allow one module
to be written with little knowledge of the code in
another module, and (2) allow modules to be reas-
sembled and replaced without reassembly of the whole
system. This facility is extremely valuable for the
production of large pieces of code, but the systems most
often used as examples of problem systems are highly-
modularized programs and make use of the techniques
mentioned above.
1Reprinted by permission of Prentice-Hall, Englewood
Cliffs, N.J.
Communications December 1972
of Volume 15
the ACM Number 12
Parnas72(b)
• Popularizes information-hiding modules
• See Parnas72a: “Information Distribution Aspects of Design
Methodology"
• Topic:
• Architecture? Well…
• Work assignments!
• Documentation!
• (Parnas72a)
Parnas72(b)
• The documentation story
• Philips Computer Industry
• A manager asked for help on creating work assignments
• How to create specifications so that modules would
integrate successfully
• Difficult because the modules had to know a lot about
each other
• How to properly decompose into modules?
Modularization
• A module is a work/responsibility assignment
• A class, a D module, a component, etc.
• “The modularizations include the design decisions
which must be made before the work on
independent modules can begin”
• “Architecture is the set of design decisions that
must be made early in a project” (Fowler, “Who
Needs an Architect?”, 2003)
Example
• KWIC Index program
• First, two Parnas modularizations
• Then, an idiomatic D modularization
KWIC
• “The KWIC index system accepts an ordered
set of lines, each line is an ordered set of
words, and each word is an ordered set of
characters. Any line may be “circularly shifted”
by repeatedly removing the first word and
appending it at the end of the line. The KWIC
index system outputs a listing of all circular
shifts of all the lines in alphabetical order”
KWIC
KWIC
KWIC
Descent of Man
The Ascent of Man
The Old Man and The Sea
A Portrait of The Artist As a Young Man
(Key Word In Context)
https://users.cs.duke.edu/~ola/ipc/kwic.html
KWIC
a portrait of the ARTIST as a young man
the ASCENT of man
DESCENT of man
a portrait of the artist as a young MAN
descent of MAN
the ascent of MAN
the old MAN and the sea
the OLD man and the sea
a PORTRAIT of the artist as a young man
the old man and the SEA
a portrait of the artist as a YOUNG man
KWIC
a portrait of the ARTIST as a young man
the ASCENT of man
DESCENT of man
a portrait of the artist as a young MAN
descent of MAN
the ascent of MAN
the old MAN and the sea
the OLD man and the sea
a PORTRAIT of the artist as a young man
the old man and the SEA
a portrait of the artist as a YOUNG man
KWIC
a portrait of the ARTIST as a young man
the ASCENT of man
DESCENT of man
a portrait of the artist as a young MAN
descent of MAN
the ascent of MAN
the old MAN and the sea
the OLD man and the sea
a PORTRAIT of the artist as a young man
the old man and the SEA
a portrait of the artist as a YOUNG man
KWIC
A Portrait of The Artist As a Young Man
The Ascent of Man
Descent of Man
A Portrait of The Artist As a Young Man
Descent of Man
The Ascent of Man
The Old Man and The Sea
The Old Man and The Sea
A Portrait of The Artist As a Young Man
The Old Man and The Sea
A Portrait of The Artist As a Young Man
KWIC
Artist As a Young Man, A Portrait of The
Ascent of Man, The
Descent of Man
Man, A Portrait of The Artist As a Young
Man, Descent of
Man, The Ascent of
Man and The Sea, The Old
Old Man and The Sea, The
Portrait of The Artist As a Young Man, A
Sea, The Old Man and The
Young Man, A Portrait of The Artist As a
KWIC
Artist As a Young Man, A Portrait of The
Ascent of Man, The
Descent of Man
Man, A Portrait of The Artist As a Young
Man, Descent of
Man, The Ascent of
Man and The Sea, The Old
Old Man and The Sea, The
Portrait of The Artist As a Young Man, A
Sea, The Old Man and The
Young Man, A Portrait of The Artist As a
KWIC
Artist As a Young Man A Portrait of The
Ascent of Man The
Descent of Man
Man A Portrait of The Artist As a Young
Man Descent of
Man The Ascent of
Man and The Sea The Old
Old Man and The Sea The
Portrait of The Artist As a Young Man A
Sea The Old Man and The
Young Man A Portrait of The Artist As a
KWIC
Artist As a Young Man A Portrait of The
Ascent of Man The
Descent of Man
Man A Portrait of The Artist As a Young
Man and The Sea The Old
Man Descent of
Man The Ascent of
Old Man and The Sea The
Portrait of The Artist As a Young Man A
Sea The Old Man and The
Young Man A Portrait of The Artist As a
KWIC
A Portrait of The Artist As a Young Man
a Young Man A Portrait of The Artist As
and The Sea The Old Man
Artist As a Young Man A Portrait of The
As a Young Man A Portrait of The Artist
Ascent of Man The
Descent of Man
Man A Portrait of The Artist As a Young
Man and The Sea The Old
Man Descent of
Man The Ascent of
of Man Descent
of Man The Ascent
of The Artist As a Young Man A Portrait
Old Man and The Sea The
Portrait of The Artist As a Young Man A
Sea The Old Man and The
The Artist As a Young Man A Portrait of
The Ascent of Man
The Old Man and The Sea
The Sea The Old Man and
Young Man A Portrait of The Artist As a
KWIC
A Portrait of The Artist As a Young Man
a Young Man A Portrait of The Artist As
and The Sea The Old Man
Artist As a Young Man A Portrait of The
As a Young Man A Portrait of The Artist
Ascent of Man The
Descent of Man
Man A Portrait of The Artist As a Young
Man and The Sea The Old
Man Descent of
Man The Ascent of
of Man Descent
of Man The Ascent
of The Artist As a Young Man A Portrait
Old Man and The Sea The
Portrait of The Artist As a Young Man A
Sea The Old Man and The
The Artist As a Young Man A Portrait of
The Ascent of Man
The Old Man and The Sea
The Sea The Old Man and
Young Man A Portrait of The Artist As a
KWIC
• Input: a sequence of lines
• Line: a sequence of words
• Word: a sequence of characters
• Circular shift:
• foo bar baz → baz foo bar
• Output: all circular shifts of all lines, in alphabetical
order
A Tale of 2 Decompositions
• Parnas’ two decompositions reimplemented in D
• <https://github.com/luismarques/parnas72>
Decomposition 1 (D1)
• Idea:
• The flowchart method
• The classic method
• Data-oriented design
• Module == collection of procedures
Decomposition 1 (D1)
canonical representation of input
all circular shifts of input lines

(no order requirements)
all circular shifts of input lines

in alphabetical order
pretty printed index
control
Output
Alphabetizer
Circular
shifter
Input
input data
D1 - Input
• Module 1: Input. This module reads the data lines
from the input medium and stores them in core for
processing by the remaining modules. The
characters are packed four to a word, and an
otherwise unused character is used to indicate the
end of a word. An index is kept to show the starting
address of each line.
D1 - Input
Foo
import std.utf;
public:
// change these definitions, for different performance
alias LineNum = ptrdiff_t;
alias WordNum = ptrdiff_t;
alias CharNum = ptrdiff_t;
enum wordSeparator = ' ';
string data;
CharNum[] lineIndex;
// Reads the lines from the input medium, and stores e
// `wordSeparator` character constant. The Lines are s
// and a separate index of where the lines start is ke
void readLines(string filename)
{
F o o ␣
D 0 0 0
F o o ␣ D
L u í s
{
1 char / 1 dchar
{
2 char / 1 dchar
•••• •••• •
•••• ••••
D1 - Input
• Input:
Descent␣of␣Man↵The⇥Ascent⇥␣of␣Man␣␣↵The␣
Old␣Man␣and␣The␣Sea↵␣␣↵A␣Portrait␣of␣The␣Ar
tist␣As␣a␣Young␣Man
• Output:
Descent␣of␣ManThe␣Ascent␣of␣ManThe␣Old␣Man␣
and␣The␣SeaA␣Portrait␣of␣The␣Artist␣As␣a␣You
ng␣Man

[0, 14, 31, 54]
// change these definitions, for different performance/capability trade-offs:
alias LineNum = ptrdiff_t;
alias WordNum = ptrdiff_t;
alias CharNum = ptrdiff_t;
enum wordSeparator = ' ';
string data;
CharNum[] lineIndex;
// Reads the lines from the input medium, and stores each word separated by a
// `wordSeparator` character constant. The Lines are stored without a separator,
// and a separate index of where the lines start is kept in `lineIndex`.
void readLines(string filename)
{
size_t lineStart;
data = readText(filename)
.lineSplitter
// normalize the spacing between words
.map!(line => line
.splitter!isWhite
.filter!(c => !c.empty)
.joiner([wordSeparator]))
// remove empty lines
.filter!(line => !line.empty)
// keep an index of where each line starts
.tee!((line) {
lineIndex ~= lineStart;
lineStart += line.byChar.walkLength;
})
// join the lines
.joiner
.to!string;
}
D1 - Input
// Reads the lines from the input medium, and stores each word separated by a
// `wordSeparator` character constant. The Lines are stored without a separator,
// and a separate index of where the lines start is kept in `lineIndex`.
void readLines(string filename)
{
size_t lineStart;
data = readText(filename)
.lineSplitter
// normalize the spacing between words
.map!(line => line
.splitter!isWhite
.filter!(c => !c.empty)
.joiner([wordSeparator]))
// remove empty lines
.filter!(line => !line.empty)
// keep an index of where each line starts
.tee!((line) {
lineIndex ~= lineStart;
lineStart += line.byChar.walkLength;
})
// join the lines
.joiner
.to!string;
}
data = readText(filename)
.lineSplitter
// normalize the spacing between words
.map!(line => line
.splitter!isWhite
.filter!(c => !c.empty)
.joiner([wordSeparator]))
// remove empty lines
.filter!(line => !line.empty)
// keep an index of where each line starts
.tee!((line) {
lineIndex ~= lineStart;
lineStart += line.byChar.walkLength;
})
Descent␣of␣Man↵The⇥Ascent⇥␣of␣Man␣␣↵The␣Old␣M
an␣and␣The␣Sea↵␣␣↵A␣Portrait␣of␣The␣Artist␣As␣a␣
Young␣Man


[Descent␣of␣Man][The⇥Ascent⇥␣of␣Man␣␣]
[The␣Old␣Man␣and␣The␣Sea][␣␣]
[A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man]
// normalize the spacing between words
.map!(line => line
.splitter!isWhite
.filter!(c => !c.empty)
.joiner([wordSeparator]))
// remove empty lines
.filter!(line => !line.empty)
// keep an index of where each line starts
.tee!((line) {
lineIndex ~= lineStart;
lineStart += line.byChar.walkLength;
})
// join the lines
.joiner
.to!string;
[Descent␣of␣Man][The⇥Ascent⇥␣of␣Man␣␣]
[The␣Old␣Man␣and␣The␣Sea][␣␣]
[A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man]
[[Descent][of][Man]][[The][Ascent][][of]
[Man][][]][[The][Old][Man][and][The][Sea]]
[[][][]][[A][Portrait][of][The][Artist]
[As][a][Young][Man]]
// normalize the spacing between words
.map!(line => line
.splitter!isWhite
.filter!(c => !c.empty)
.joiner([wordSeparator]))
// remove empty lines
.filter!(line => !line.empty)
// keep an index of where each line starts
.tee!((line) {
lineIndex ~= lineStart;
lineStart += line.byChar.walkLength;
})
// join the lines
.joiner
.to!string;
[[Descent][of][Man]][[The][Ascent][][of]
[Man][][]][[The][Old][Man][and][The][Sea]]
[[][][]][[A][Portrait][of][The][Artist]
[As][a][Young][Man]]
[[Descent][of][Man]][[The][Ascent][of]
[Man]][[The][Old][Man][and][The][Sea]][]
[[A][Portrait][of][The][Artist][As][a]
[Young][Man]]
// normalize the spacing between words
.map!(line => line
.splitter!isWhite
.filter!(c => !c.empty)
.joiner([wordSeparator]))
// remove empty lines
.filter!(line => !line.empty)
// keep an index of where each line starts
.tee!((line) {
lineIndex ~= lineStart;
lineStart += line.byChar.walkLength;
})
// join the lines
.joiner
.to!string;
[[Descent][of][Man]][[The][Ascent][of]
[Man]][[The][Old][Man][and][The][Sea]][]
[[A][Portrait][of][The][Artist][As][a]
[Young][Man]]
[Descent␣of␣Man][The␣Ascent␣of␣Man]
[The␣Old␣Man␣and␣The␣Sea][]
[A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man]
// remove empty lines
.filter!(line => !line.empty)
// keep an index of where each line starts
.tee!((line) {
lineIndex ~= lineStart;
lineStart += line.byChar.walkLength;
})
// join the lines
.joiner
.to!string;
}
[Descent␣of␣Man][The␣Ascent␣of␣Man]
[The␣Old␣Man␣and␣The␣Sea][]
[A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man]


[Descent␣of␣Man][The␣Ascent␣of␣Man]
[The␣Old␣Man␣and␣The␣Sea]
[A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man]
// remove empty lines
.filter!(line => !line.empty)
// keep an index of where each line starts
.tee!((line) {
lineIndex ~= lineStart;
lineStart += line.byChar.walkLength;
})
// join the lines
.joiner
.to!string;
}
Descent␣of␣ManThe␣Ascent␣of␣ManThe␣Old␣Man␣
and␣The␣SeaA␣Portrait␣of␣The␣Artist␣As␣a␣You
ng␣Man
D1 - Circular Shift
• Module 2: Circular Shift. This module is called
after the input module has completed its work. It
prepares an index which gives the address of the
first character of each circular shift, and the original
index of the line in the array made up by module 1.
It leaves its output in core with words in pairs
(original line number, starting address).
D1 - Circular Shift
import std.algorithm;
import std.range;
import std.typecons;
import std.utf;
import one.input;
public:
struct ShiftIndexEntry
{
LineNum lineNum;
CharNum firstChar;
}
ShiftIndexEntry[] shiftIndex;
void setup()
{
shiftIndex = iota(lineIndex.length) // 0
D1 - Circular Shift
Circular
shifter
Input
.map!(a => a.value.byCodeUnit
.enumerate // (0, ((0, char1), (1, char2
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
}
private:
auto line(LineNum lineNum)
{
auto lineStart = lineIndex[lineNum];
auto lineEnd = lineNum+1 >= lineIndex.length ?
data.length : lineIndex[lineNum+1];
return data[lineStart .. lineEnd];
}
D1 - Circular Shift
{
LineNum lineNum;
CharNum firstChar;
}
ShiftIndexEntry[] shiftIndex;
void setup()
{
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
}
private:
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
0, 1, …
[Descent␣of␣Man]

[The␣Ascent␣of␣Man]
…
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
(0, [Descent␣of␣Man])

(1, [The␣Ascent␣of␣Man])
[Descent␣of␣Man]

[The␣Ascent␣of␣Man]
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
[0, 8, 11]

[14, 18, 25, 28]
(0, [Descent␣of␣Man])

(1, [The␣Ascent␣of␣Man])
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
(0, [Descent␣of␣Man])

(1, [The␣Ascent␣of␣Man])
0 1 2 3 4 5 6 7 8 9
10
11
12
13
(0, [[Descent][of]
(1, [[The][Ascent]
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
0 1 2 3 4 5 6 8 9
(0, [Descent␣of␣Man])

(1, [The␣Ascent␣of␣Man])
0 1 2 3 4 5 6 7 8 9
10
11
12
13
(0, [[Descent][of][Man]])

(1, [[The][Ascent][of][Man]])
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
0 1 2 3 4 5 6 8 9 11 13
[0, 8, 11]

[14, 18, 25, 28]
(0, [0, 8, 11])
(1, [14, 18, 25, 28])
[0, 8, 11]

[14, 18, 25, 28]
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
(0, [0, 8, 11])
(1, [14, 18, 25, 28])
[
ShiftIndexEntry(0, 0),

ShiftIndexEntry(0, 8), 

ShiftIndexEntry(0, 11)
]
[
ShiftIndexEntry(1, 14),
ShiftIndexEntry(1, 18),
ShiftIndexEntry(1, 25),
ShiftIndexEntry(1, 28)
]
shiftIndex = iota(lineIndex.length)
.map!(lineNum => line(lineNum))
.enumerate
.map!(a => a.value.byCodeUnit
.enumerate
.splitter!(b => b.value == wordSeparator)
.map!(b => b.front.index + lineIndex[a.index]))
.enumerate
.map!(a => a.value
.map!(b => ShiftIndexEntry(a.index, b)))
.joiner
.array;
vate:
o line(LineNum lineNum)
ShiftIndexEntry(0, 0)

ShiftIndexEntry(0, 8) 

ShiftIndexEntry(0, 11)
ShiftIndexEntry(1, 14)
ShiftIndexEntry(1, 18)
ShiftIndexEntry(1, 25)
ShiftIndexEntry(1, 28)
D1 - Alphabetizing
• Module 3: Alphabetizing. This module takes as
input the arrays produced by modules 1 and 2. It
produces an array in the same format as that
produced by module 2. In this case, however, the
circular shifts are listed in another order
(alphabetically).
D1 - Alphabetizing
import std.typecons;
import std.utf;
import one.input;
public:
struct ShiftIndexEntry
{
LineNum lineNum;
CharNum firstChar;
}
ShiftIndexEntry[] shiftIndex;
void setup()
{
shiftIndex = iota(lineIndex.length) // 0, 1, 2 ...
.map!(lineNum => line(lineNum)) // first line, se
.enumerate // (0, first line), (1, secon
.map!(a => a.value.byCodeUnit
[The␣Ascent␣of␣Man]
[Ascent␣of␣Man␣The]
[Ascent␣of␣Man][The]
[The␣Ascent␣of␣Man]
[Ascent␣of␣Man␣The]
[Ascent␣of␣Man][The]
[The␣Ascent␣of␣Man]
[Ascent␣of␣Man␣The]
private:
auto line(ShiftIndexEntry entry)
{
auto a = entry.firstChar;
auto b = entry.lineNum+1 >= lineIndex.length ?
data.length : lineIndex[entry.lineNum+1];
auto c = lineIndex[entry.lineNum];
auto d = (entry.firstChar-1).max(0).max(c);
auto x = data[a .. b];
auto y = data[c .. d];
return joiner(only(x, y).filter!(a => !a.empty), " ");
}
c d a b
y x
D1 - Alphabetizing
module one.alphabetizer;
import std.algorithm;
import std.math;
import std.range;
import std.typecons;
import std.uni;
import std.utf;
import one.input;
import one.circularshifter;
public:
void setup()
{
shiftIndex.sort!((a, b) => icmp(line(a), line(b)) < 0);
}
private:
auto line(ShiftIndexEntry entry)
{
auto a = entry.firstChar;
auto b = entry.lineNum+1 >= lineIndex.length ? data.length :
lineIndex[entry.lineNum+1];
D1 - Output
• Module 4: Output. Using the arrays produced by
module 3 and module 1, this modules produces a
nicely formatted output listing all of the circular
shifts. In a sophisticated system the actual start of
each line will be marked, pointers to further
information may be inserted, and the start of the
circular shift may actually not be the first word in
the line, etc.
D1 - Output
void printLines()
{
shiftIndex.map!(entry => entry.line).each!writeln;
}
private:
auto line(ShiftIndexEntry entry)
{
auto a = entry.firstChar;
auto b = entry.lineNum+1 >= lineIndex.length ?
data.length : lineIndex[entry.lineNum+1];
auto c = lineIndex[entry.lineNum];
auto d = (entry.firstChar-1).max(0).max(c);
auto x = data[a .. b];
auto y = data[c .. d];
return joiner(only(x, y).filter!(a => !a.empty), " ");
}
D1 - Outputmodule one.output;
import std.algorithm;
import std.range;
import std.stdio;
import one.input;
import one.circularshifter;
void printLines()
{
shiftIndex.map!(entry => entry.line).each!writeln;
}
private:
auto line(ShiftIndexEntry entry)
{
auto a = entry.firstChar;
auto b = entry.lineNum+1 >= lineIndex.length ?
data.length : lineIndex[entry.lineNum+1];
D1 - Master Control
• Module 5: Master Control. This module does little
more than control the sequencing among the other
four modules. It may also handle error messages,
space allocation, etc.
D1 - Master Control
module one.control;
import one.input;
import one.circularshifter;
import one.alphabetizer;
import one.output;
public:
void run(string inputFile)
{
readLines(inputFile);
one.circularshifter.setup();
one.alphabetizer.setup();
printLines();
}
D1 - Result
A Portrait of The Artist As a Young Man
a Young Man A Portrait of The Artist As
and The Sea The Old Man
Artist As a Young Man A Portrait of The
As a Young Man A Portrait of The Artist
Ascent of Man The
Descent of Man
Man A Portrait of The Artist As a Young
Man and The Sea The Old
Man Descent of
Man The Ascent of
of Man Descent
of Man The Ascent
of The Artist As a Young Man A Portrait
Old Man and The Sea The
Portrait of The Artist As a Young Man A
Sea The Old Man and The
The Artist As a Young Man A Portrait of
The Ascent of Man
The Old Man and The Sea
The Sea The Old Man and
Young Man A Portrait of The Artist As a
Decomposition 2 (D2)
• Based on:
• Difficult design decisions, or design decisions
which are likely to change (the secret)
• Modules are designed to hide these decisions
from the others
• Abstract interface
• Efficient work partition
Decomposition 2 (D2)
control
Output
Alphabetizer
Circular
shifter
Input
Line Storage
D2 - Line Storage
• Module 1: Line Storage. This module consists of a number of
functions(…).
• WORD
• SETWRD
• WORDS
• LINES
• DELWRD
• DELLINE
• CHARS
D2 - Line Storage
Definition of a "Line Storage" Module
Introduction: This definition specifies a mechanism which
hold up to pi lines, each line consisting of up to p2 word
may be up to p3 characters.
Function WORD
possible values: integers
initial values: undefined
parameters: l»w,c all
effect: j .
call
call
call
call
call
call
ERLWEL
ERLWNL
ERLWEW
ERLWNW
ERLWEC
ERLWNC
integer
if
if
if
if
if
if
1 <
1 >
w <
w >
c <
c >
1 or 1 >
LINES
1 or w >
WORDS(1)
1 or c >
CHARS(l,w)
P2
P3
Function SETWRD
possible values:
initial values:
none
not applicable
D2 - Line Storage
call
call
call
call
call
call
ERLWEL
ERLWNL
ERLWEW
ERLWNW
ERLWEC
ERLWNC
if
if
if
if
if
if
1 <
1 >
w <
w >
c <
c >
1 or 1 >
LINES
1 or w >
WORDS(1)
1 or c >
CHARS(l,w)
P2
P3
Function SETWRD
possible values:
initial values:
parameters:
effect:
none
not applicable
l,w,c,d all integers
call
call
call
call
call
call
call
call
if 1
if w
ERLSLE
ERLSBL
ERLSBL
ERLSWE
ERLSBW
ERLSBW
ERLSCE
ERLSBC
= 'LINES'
1 or 1 > pl
'LINES' +1
'LINES'
1 or w > p2
'WORDS'(1) + 1
'WORDS'(1)
1 or c > p3
if c .noteq. 'CHARS'(l,w)+l
+1 then LINES = 'LINES* + 1
if 1 <
if 1 >
if 1 <
if w <
if w >
if w <
if c <
= 'WORDS*(1) +1 then WORDS(1)
CHARS(l,w) = c
WORDQ,w,c) = d
Function WORDS
D2 - Line Storage
Definition of a "Line Storage" Module
Introduction: This definition specifies a mechanism which
hold up to pi lines, each line consisting of up to p2 word
may be up to p3 characters.
Function WORD
possible values: integers
initial values: undefined
parameters: l»w,c all
effect: j .
call
call
call
call
call
call
ERLWEL
ERLWNL
ERLWEW
ERLWNW
ERLWEC
ERLWNC
integer
if
if
if
if
if
if
1 <
1 >
w <
w >
c <
c >
1 or 1 >
LINES
1 or w >
WORDS(1)
1 or c >
CHARS(l,w)
P2
P3
Function SETWRD
possible values:
initial values:
none
not applicable
CHAR
r
D2 - Line Storage
• The function call CHAR(r,w,c) will have as value an
integer representing the cth character in the rth line,
wth word
• A call such as SETCHAR(r,w,c,d) will cause the cth
character in the wth word of the rth line to be the
character represented by d (i.e., CHAR(r,w,c) = d)
• WORDS(r) returns the number of words in line r
• Etc.
D2 - Line Storage
• Functions
• CHAR
• SETCHAR
• WORDS
• LINES
• DELWRD
• DELLINE
• CHARS
D2 - Line Storage
module two.linestorage;
import std.algorithm;
import std.range;
import std.utf;
public:
// change these definitions, for different performance/capabili
alias LineNum = ptrdiff_t;
alias WordNum = ptrdiff_t;
alias CharNum = ptrdiff_t;
enum maxLines = LineNum.max; /// (original name: p1)
enum maxWordsPerLine = WordNum.max; /// (original name: p2)
enum maxCharsPerWord = CharNum.max; /// (original name: p3)
/// Returns one character from a given word, present at a given
/// (original name: WORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum
D2 - Line Storage
void removeLine(LineNum lineNum)
{
assert(lineNum >= 0 && lineNum < numLine
assert(false, "not implemented");
}
// --
private:
enum wordSeparator = ' ';
char[] data;
CharNum[] lineIndex;
auto line(LineNum lineNum)
{
auto lineStart = lineIndex[lineNum];
auto lineEnd = lineNum+1 >= lineIndex.le
private:
D2 - Line Storage
// --
private:
enum wordSeparator = ' ';
char[] data;
CharNum[] lineIndex;
auto line(LineNum lineNum)
{
auto lineStart = lineIndex[lineNum];
auto lineEnd = lineNum+1 >= lineIndex.length ?
data.length : lineIndex[lineNum+1];
return data[lineStart .. lineEnd].byCodeUnit;
}
/// Returns a range of words for a given line
auto wordsForLine(LineNum lineNum)
{
return line(lineNum).splitter(wordSeparator);
}
private:
D2 - Line Storage
alias CharNum = ptrdiff_t;
enum maxLines = LineNum.max; /// (original name: p1)
enum maxWordsPerLine = WordNum.max; /// (original name: p2)
enum maxCharsPerWord = CharNum.max; /// (original name: p3)
/// Returns one character from a given word, from line `lineNum`.
/// (original name: WORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum)
{
assert(lineNum >= 0 && lineNum < maxLines);
assert(lineNum < numLines);
assert(wordNum >= 0 && wordNum < maxWordsPerLine);
assert(wordNum < numWords(lineNum));
assert(charNum >= 0 && charNum < maxCharsPerWord);
assert(charNum < numCharacters(lineNum, wordNum));
return wordsForLine(lineNum)
.dropExactly(wordNum)
.front
.dropExactly(charNum)
.front;
}
/// Returns one character from a given word, from line `lineNum`.
/// (original name: WORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum)
{
assert(lineNum >= 0 && lineNum < maxLines);
assert(lineNum < numLines);
assert(wordNum >= 0 && wordNum < maxWordsPerLine);
assert(wordNum < numWords(lineNum));
assert(charNum >= 0 && charNum < maxCharsPerWord);
assert(charNum < numCharacters(lineNum, wordNum));
return wordsForLine(lineNum)
.dropExactly(wordNum)
.front
.dropExactly(charNum)
.front;
}
/// Sets the next character for the current or the next word.
/// The current word is the last word present at the last line.
/// (original name: SETWRD)
void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch
{
assert(lineNum >= 0 && lineNum < maxLines);
/// Returns one character from a given word, from line `lineNum`.
/// (original name: WORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum)
{
assert(lineNum >= 0 && lineNum < maxLines);
assert(lineNum < numLines);
assert(wordNum >= 0 && wordNum < maxWordsPerLine);
assert(wordNum < numWords(lineNum));
assert(charNum >= 0 && charNum < maxCharsPerWord);
assert(charNum < numCharacters(lineNum, wordNum));
return wordsForLine(lineNum)
.dropExactly(wordNum)
.front
.dropExactly(charNum)
.front;
}
/// Sets the next character for the current or the next word.
/// The current word is the last word present at the last line.
/// (original name: SETWRD)
void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch
{
assert(lineNum >= 0 && lineNum < maxLines);
f o o a rb
/// Returns one character from a given word, from line `lineNum`.
/// (original name: WORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum)
{
assert(lineNum >= 0 && lineNum < maxLines);
assert(lineNum < numLines);
assert(wordNum >= 0 && wordNum < maxWordsPerLine);
assert(wordNum < numWords(lineNum));
assert(charNum >= 0 && charNum < maxCharsPerWord);
assert(charNum < numCharacters(lineNum, wordNum));
return wordsForLine(lineNum)
.dropExactly(wordNum)
.front
.dropExactly(charNum)
.front;
}
/// Sets the next character for the current or the next word.
/// The current word is the last word present at the last line.
/// (original name: SETWRD)
void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch
{
assert(lineNum >= 0 && lineNum < maxLines);
f o o a rb
/// Returns one character from a given word, from line `lineNum`.
/// (original name: WORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum)
{
assert(lineNum >= 0 && lineNum < maxLines);
assert(lineNum < numLines);
assert(wordNum >= 0 && wordNum < maxWordsPerLine);
assert(wordNum < numWords(lineNum));
assert(charNum >= 0 && charNum < maxCharsPerWord);
assert(charNum < numCharacters(lineNum, wordNum));
return wordsForLine(lineNum)
.dropExactly(wordNum)
.front
.dropExactly(charNum)
.front;
}
/// Sets the next character for the current or the next word.
/// The current word is the last word present at the last line.
/// (original name: SETWRD)
void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch
{
assert(lineNum >= 0 && lineNum < maxLines);
a rb
/// Returns one character from a given word, from line `lineNum`.
/// (original name: WORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum)
{
assert(lineNum >= 0 && lineNum < maxLines);
assert(lineNum < numLines);
assert(wordNum >= 0 && wordNum < maxWordsPerLine);
assert(wordNum < numWords(lineNum));
assert(charNum >= 0 && charNum < maxCharsPerWord);
assert(charNum < numCharacters(lineNum, wordNum));
return wordsForLine(lineNum)
.dropExactly(wordNum)
.front
.dropExactly(charNum)
.front;
}
/// Sets the next character for the current or the next word.
/// The current word is the last word present at the last line.
/// (original name: SETWRD)
void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch
{
assert(lineNum >= 0 && lineNum < maxLines);
a rb
/// Returns one character from a given word, from line `lineNum`.
/// (original name: WORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum)
{
assert(lineNum >= 0 && lineNum < maxLines);
assert(lineNum < numLines);
assert(wordNum >= 0 && wordNum < maxWordsPerLine);
assert(wordNum < numWords(lineNum));
assert(charNum >= 0 && charNum < maxCharsPerWord);
assert(charNum < numCharacters(lineNum, wordNum));
return wordsForLine(lineNum)
.dropExactly(wordNum)
.front
.dropExactly(charNum)
.front;
}
/// Sets the next character for the current or the next word.
/// The current word is the last word present at the last line.
/// (original name: SETWRD)
void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch
{
assert(lineNum >= 0 && lineNum < maxLines);
a r
/// Sets the next character for the current or the next word.
/// The current word is the last word present at the last line.
/// (original name: SETWRD)
void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, char charValue)
{
assert(lineNum >= 0 && lineNum < maxLines);
assert(lineNum == numLines-1 || lineNum == numLines);
assert(wordNum >= 0 && wordNum < maxWordsPerLine);
assert(charNum >= 0 && charNum < maxCharsPerWord);
if(lineNum < numLines)
{
auto nw = numWords(lineNum);
assert(wordNum == nw-1 || wordNum == nw);
if(wordNum < nw)
{
assert(charNum == numCharacters(lineNum, wordNum));
}
}
if(lineNum == numLines)
{
lineIndex ~= data.length;
}
else
{
if(wordNum == numWords(lineNum))
{
data ~= wordSeparator;
}
}
data ~= charValue;
}
D2 - Line Storage
/// Sets the next character for the current or the next word.
/// The current word is the last word present at the last line.
/// (original name: SETWRD)
void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, char charValue)
{
if(lineNum == numLines)
{
lineIndex ~= data.length;
}
else
{
if(wordNum == numWords(lineNum))
{
data ~= wordSeparator;
}
}
data ~= charValue;
}
D2 - Input
• Module 2: Input. This module reads the original
lines from the input media and calls the line storage
module to have them stored internally.
D2 - Inputmodule two.input;
import std.stdio;
import std.uni;
import two.linestorage;
public:
void readLines(string inputFile)
{
foreach(lineNum, line; File(inputFile).byLine)
{
foreach(charNum, c; line)
{
setWordChar(lineNum, wordNum, charNum, c);
}
}
}
void readLines(string inputFile)
{
LineNum lineNum;
foreach(line; File(inputFile).byLine)
{
WordNum wordNum;
CharNum charNum;
bool incWordNum;
bool incLineNum;
foreach(c; line)
{
if(c.isWhite)
incWordNum = true;
else
{
if(incWordNum)
{
wordNum++;
charNum = 0;
incWordNum = false;
}
setWordChar(lineNum, wordNum, charNum, c);
charNum++;
incLineNum = true;
}
}
if(incLineNum)
{
lineNum++;
incLineNum = false;
}
}
}
D2 - Circular Shifter
• Module 3: Circular Shifter. The principal functions provided by this
module are analogs of functions provided in module 1. (…)
• CHAR → CSCHAR
• WORDS → CSWORDS
• LINES → CSLINES
• CHARS → CSCHARS
• …
• A function CSSETUP is provided which must be called before the
other functions have their value specified.
D2 - Circular Shifter
private:
WordNum storageWordNum = (entry.firstWor
storage.numWords(entry.lineNum);
return storage.numCharacters(entry.lineN
}
// --
private:
struct ShiftIndexEntry
{
LineNum lineNum;
WordNum firstWord;
}
ShiftIndexEntry[] shiftIndex;
D2 - Circular Shifter
public:
enum maxLines = LineNum.max; /// (original name: p4)
alias LineNum = storage.LineNum;
alias WordNum = storage.WordNum;
alias CharNum = storage.CharNum;
/// (original name: CSSTUP)
void setup()
{
shiftIndex = iota(storage.numLines)
.map!(a => storage.numWords(a).iota
.map!(b => ShiftIndexEntry(a, b)))
.joiner
.array;
}
/// (original name: CSWORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNu
{
/// (original name: CSSTUP)
void setup()
{
shiftIndex = iota(storage.numLines)
.map!(a => storage.numWords(a).iota
.map!(b => ShiftIndexEntry(a, b)))
.joiner
.array;
}
/// (original name: CSWORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNu
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + wordNu
storage.numWords(entry.lineNum);
return storage.wordChar(entry.lineNum, storageWord
}
/// (original name: CSWRDS)
/// (original name: CSSTUP)
void setup()
{
shiftIndex = iota(storage.numLines)
.map!(a => storage.numWords(a).iota
.map!(b => ShiftIndexEntry(a, b)))
.joiner
.array;
}
/// (original name: CSWORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNu
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + wordNu
storage.numWords(entry.lineNum);
return storage.wordChar(entry.lineNum, storageWord
}
/// (original name: CSWRDS)
0, 1, …
/// (original name: CSSTUP)
void setup()
{
shiftIndex = iota(storage.numLines)
.map!(a => storage.numWords(a).iota
.map!(b => ShiftIndexEntry(a, b)))
.joiner
.array;
}
/// (original name: CSWORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNu
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + wordNu
storage.numWords(entry.lineNum);
return storage.wordChar(entry.lineNum, storageWord
}
/// (original name: CSWRDS)
0, 1, …
3 words, 4 words, …
/// (original name: CSSTUP)
void setup()
{
shiftIndex = iota(storage.numLines)
.map!(a => storage.numWords(a).iota
.map!(b => ShiftIndexEntry(a, b)))
.joiner
.array;
}
/// (original name: CSWORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNu
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + wordNu
storage.numWords(entry.lineNum);
return storage.wordChar(entry.lineNum, storageWord
}
/// (original name: CSWRDS)
0, 1, …
3 words, 4 words, …
[0, 1, 2], [0, 1, 2, 3], …
/// (original name: CSSTUP)
void setup()
{
shiftIndex = iota(storage.numLines)
.map!(a => storage.numWords(a).iota
.map!(b => ShiftIndexEntry(a, b)))
.joiner
.array;
}
/// (original name: CSWORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNu
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + wordNu
storage.numWords(entry.lineNum);
return storage.wordChar(entry.lineNum, storageWord
}
/// (original name: CSWRDS)
0, 1, …
3 words, 4 words, …
[0, 1, 2], [0, 1, 2, 3], …
[ShiftIndexEntry(0, 0), ShiftIndexEntry(0, 1), …]
[…, ShiftIndexEntry(1, 2), ShiftIndexEntry(1, 3)]

…
/// (original name: CSSTUP)
void setup()
{
shiftIndex = iota(storage.numLines)
.map!(a => storage.numWords(a).iota
.map!(b => ShiftIndexEntry(a, b)))
.joiner
.array;
}
/// (original name: CSWORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNu
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + wordNu
storage.numWords(entry.lineNum);
return storage.wordChar(entry.lineNum, storageWord
}
/// (original name: CSWRDS)
ShiftIndexEntry(0, 0)
ShiftIndexEntry(0, 1)
ShiftIndexEntry(0, 3)
ShiftIndexEntry(1, 0)
ShiftIndexEntry(1, 1)
ShiftIndexEntry(1, 2)
ShiftIndexEntry(1, 3)
…
D2 - Circular Shifter
/// (original name: CSWRDS)
LineNum numWords(LineNum lineNum)
{
auto entry = shiftIndex[lineNum];
return storage.numWords(entry.lineNum);
}
/// (original name: CSLNES)
LineNum numLines()
{
return shiftIndex.length.to!LineNum;
}
/// (original name: CSCHRS)
CharNum numCharacters(LineNum lineNum, WordNum wordN
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + word
storage.numWords(entry.lineNum);
}
/// (original name: CSWORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharN
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + wordN
storage.numWords(entry.lineNum);
return storage.wordChar(entry.lineNum, storageWor
}
/// (original name: CSWRDS)
LineNum numWords(LineNum lineNum)
{
auto entry = shiftIndex[lineNum];
return storage.numWords(entry.lineNum);
}
D2 - Circular Shifter
void setup()
{
shiftIndex = iota(storage.numLines)
.map!(a => storage.numWords(a).iota
.map!(b => ShiftIndexEntry(a, b)))
.joiner
.array;
}
/// (original name: CSWORD)
char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum)
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + wordNum) %
storage.numWords(entry.lineNum);
return storage.wordChar(entry.lineNum, storageWordNum, charNum);
}
/// (original name: CSWRDS)
LineNum numWords(LineNum lineNum)
{
auto entry = shiftIndex[lineNum];
D2 - Circular Shifter
/// (original name: CSCHRS)
CharNum numCharacters(LineNum lineNum, WordNum wordNum)
{
auto entry = shiftIndex[lineNum];
WordNum storageWordNum = (entry.firstWord + wordNum) %
storage.numWords(entry.lineNum);
return storage.numCharacters(entry.lineNum, storageWordNum);
}
// --
private:
D2 - Alphabetizing
• Module 4: Alphabetizing. (…) ITH(i) will give the
index of the circular shift which comes ith in the
alphabetical ordering.
auto alphabeticIndex()
{
auto indexOffsets = new LineNum[numLines];
makeIndex!((a, b) => a.icmp(b) < 0)(numLines.iota.map!(a => l
indexOffsets);
return indexOffsets;
}
auto line(LineNum lineNum)
{
return numWords(lineNum)
.iota
.map!(wordNum => word(lineNum, wordNum))
.joiner(" ".byCodeUnit);
}
auto word(LineNum lineNum, WordNum wordNum)
{
return numCharacters(lineNum, wordNum)
.iota
.map!(charNum => wordChar(lineNum, wordNum, charNum));
}
D2 - Alphabetizing
private:
D2 - Alphabetizing
private:
ReturnType!alphabeticIndex index;
auto alphabeticIndex()
{
auto indexOffsets = new LineNum[numLines];
makeIndex!((a, b) => icmp(a, b) < 0)
(numLines.iota.map!(a => line(a).array), indexOffsets);
return indexOffsets;
}
D2 - Alphabetizing
import two.circularshifter;
public:
/// (original name: ALPH)
void setup()
{
index = alphabeticIndex;
}
/// (original name: ITH)
LineNum ithLine(LineNum lineNum)
{
assert(lineNum < index.length);
return index[lineNum];
}
// The original functions ALPHAC, EQW, ALPHW, EQL a
LineNum alphac(LineNum lineNum)
{
D2 - Output
• Module 5: Output. This module will give the
desired printing of the set of lines or circular shifts.
D2 - Output
.line)
.each!writeln;
}
// --
private:
auto line(LineNum lineNum)
{
return numWords(lineNum)
.iota
.map!(wordNum => word(lineNum, wordNum))
.joiner(" ");
}
auto word(LineNum lineNum, WordNum wordNum)
{
return numCharacters(lineNum, wordNum)
.iota
.map!(charNum => wordChar(lineNum, wordNum, charNum))
.byDchar;
}
D2 - Output
import std.stdio;
import std.utf;
import two.circularshifter;
import two.alphabetizer;
public:
void printLines()
{
numLines
.iota
.map!(lineNum => lineNum
.ithLine
.line)
.each!writeln;
}
// --
D2 - Result
A Portrait of The Artist As a Young Man
a Young Man A Portrait of The Artist As
and The Sea The Old Man
Artist As a Young Man A Portrait of The
As a Young Man A Portrait of The Artist
Ascent of Man The
Descent of Man
Man A Portrait of The Artist As a Young
Man and The Sea The Old
Man Descent of
Man The Ascent of
of Man Descent
of Man The Ascent
of The Artist As a Young Man A Portrait
Old Man and The Sea The
Portrait of The Artist As a Young Man A
Sea The Old Man and The
The Artist As a Young Man A Portrait of
The Ascent of Man
The Old Man and The Sea
The Sea The Old Man and
Young Man A Portrait of The Artist As a
Comparing Decompositions
• The runtime representation of both decompositions
might be the same
• D2 might have a performance impact
• Requires good optimizing compiler
Comparing Decompositions
• Amenability to change
• Comprehensibility
• Testability
• Parallel Development
Changeability
• Input format
• Store all data in memory
• Pack the characters
• Create an index of the shifts vs

store the actual data
• When to alphabetize
Changeability
Module D1 D2
Input ✔ ✔
Line Storage —
Circular
Shifter
Alphabetizer
Output
• Input format
• Store all data in memory
• Pack the characters
• Create an index of the shifts vs

store the actual data
• When to alphabetize
Changeability
Module D1 D2
Input ✔
Line Storage — ✔
Circular
Shifter
✔
Alphabetizer ✔
Output ✔
• Input format
• Store all data in memory
• Pack the characters
• Create an index of the shifts vs

store the actual data
• When to alphabetize
Changeability
Module D1 D2
Input ✔
Line Storage — ✔
Circular
Shifter
✔
Alphabetizer ✔
Output ✔
• Input format
• Store all data in memory
• Pack the characters
• Create an index of the shifts vs

store the actual data
• When to alphabetize
Changeability
Module D1 D2
Input
Line Storage —
Circular
Shifter
✔ ✔
Alphabetizer ✔
Output ✔
• Input format
• Store all data in memory
• Pack the characters
• Create an index of the shifts vs

store the actual data
• When to alphabetize
Changeability
Module D1 D2
Input
Line Storage —
Circular
Shifter
Alphabetizer ✔ ✔
Output ✔
• Input format
• Store all data in memory
• Pack the characters
• Create an index of the shifts vs

store the actual data
• When to alphabetize
Comprehensibility
• Example: understanding the output module
Comprehensibility
• Example: understanding the output module
• Decomposition 1
module one.output;
import std.algorithm;
import std.range;
import std.stdio;
import one.input;
import one.circularshifter;
void printLines()
{
shiftIndex.map!(entry => entry.line).each!writeln;
}
private:
auto line(ShiftIndexEntry entry)
{
auto a = entry.firstChar;
auto b = entry.lineNum+1 >= lineIndex.length ?
data.length : lineIndex[entry.lineNum+1];
Comprehensibility
• Example: understanding the output module
import one.circularshifter;
void printLines()
{
shiftIndex.map!(entry => entry.line).each!writeln;
}
private:
auto line(ShiftIndexEntry entry)
{
auto a = entry.firstChar;
auto b = entry.lineNum+1 >= lineIndex.length ?
data.length : lineIndex[entry.lineNum+1];
auto c = lineIndex[entry.lineNum];
auto d = (entry.firstChar-1).max(0).max(c);
auto x = data[a .. b];
auto y = data[c .. d];
return joiner(only(x, y).filter!(a => !a.empty), " ");
}
Comprehensibility
• Example: understanding the output module
• Decomposition 2
import std.algorithm;
import std.range;
import std.stdio;
import std.utf;
import two.circularshifter;
import two.alphabetizer;
public:
void printLines()
{
numLines
.iota
.map!(lineNum => lineNum
.ithLine
.line)
.each!writeln;
}
Comprehensibility
• Example: understanding the output module
• Decomposition 2
{
numLines
.iota
.map!(lineNum => lineNum
.ithLine
.line)
.each!writeln;
}
// --
private:
auto line(LineNum lineNum)
{
return numWords(lineNum)
.iota
.map!(wordNum => word(lineNum, wordNum))
.joiner(" ");
}
auto word(LineNum lineNum, WordNum wordNum)
{
Comprehensibility
• Example: understanding the output module
• Decomposition 2
// --
private:
auto line(LineNum lineNum)
{
return numWords(lineNum)
.iota
.map!(wordNum => word(lineNum, wordNum))
.joiner(" ");
}
auto word(LineNum lineNum, WordNum wordNum)
{
return numCharacters(lineNum, wordNum)
.iota
.map!(charNum => wordChar(lineNum, wordNum, charNum))
.byDchar;
}
Testability
• Parnas disputes the idea that information hiding is an
empirical result
• It’s a mathematical theorem:
1.You have two modules: A, B
2.You can prove A correct knowing only the interface of B
3.You change B without changing the interface
4.Then A doesn’t have to change
Testability
Module D1 D2
Input ✔
Line Storage — ✔
Circular
Shifter
✔
Alphabetizer ✔
Output ✔
Parallel Development
• Decomposition 1
 • Decomposition 2
M1

System design



M2
 …

M1

System design


M2
 …

Evaluation
• Information hiding: Yay or Nay?
• Fred Brook’s The Mythical
Man Month
Evaluation — MMM
"Show me your flowcharts and conceal your tables,
and I shall continue to be mystified. Show me your
tables, and I won’t usually need your flowcharts;
they’ll be obvious.”
Evaluation — MMM
"Parnas (…) has proposed a still more radical
solution. His thesis is that the programmer is most
effective if shielded from, rather than exposed to the
details of construction of system parts other than his
own. This presupposes that all interfaces are
completely and precisely defined. While that is
definitely sound design, dependence upon its perfect
accomplishment is a recipe for disaster.”
Evaluation — MMM
"Parnas Was Right, and I Was Wrong about Information
Hiding



(…) I am now convinced that information hiding, today
often embodied in object-oriented programming, is the
only way of raising the level of software design.

(…) [The traditional] technique ensures that programmers
can know the detailed semantics of the interfaces they
work to by knowing what is on the other side. Hiding those
semantics leads to system bugs. On the other hand,
Parnas's technique is robust under change and is more
appropriate in a design-for-change philosophy. (…)
Evaluation
• Information hiding: Yay or Nay?
• Fred Brooks 👍
• Name
• Is decomposition 2 sufficiently good?
Master, teach me the true ways of information hiding
Accidental Complexity
• We already removed some accidental complexity:
• Byte-oriented vs word-oriented
• Unicode vs weird character comparison functions
• Exceptions & assertions vs archaic error routines
• Proper naming & namespacing
• numWords vs WORDS
• CHAR, CSCHAR vs module.wordChar
Accidental Complexity
• Yet, D2 still has a lot of issues:
• Global state
• Lack of constructors / initializers
• Each function must check if we are in the uninitialized
state or in the steady state.
• Sequence Interfaces
• Memory allocation and data flow
• setWordChar(lineNum, wordNum, charNum, c);
Sequence Interface
• Input: a sequence of lines
• Line: a sequence of words
• Word: a sequence of characters
Sequence Interface
f o o ␣ b a r b a z
• Input: a sequence of lines
• Line: a sequence of words
• Word: a sequence of characters
Sequence Interface
• Input: a sequence of lines
• Line: a sequence of words
• Word: a sequence of characters
f o o
b a z
b a r
line
line
word word
word
Sequence Interface
• Functions
• CHAR
• SETCHAR
• WORDS
• LINES
• DELWRD
• DELLINE
• CHARS
f o o ␣ b a r b a z
Sequence Interface
• Functions
• CHAR
• SETCHAR
• WORDS
• LINES
• DELWRD
• DELLINE
• CHARS
f o o ␣ b a r b a z
E.g. CHAR(r,w,c)
Sequence Interface
• Functions
• DELLINE
• LINES
• DELWRD
• WORDS
• CHARS
• CHAR
• SETCHAR
Sequence Interface
• Functions
• DELLINE
• LINES
• DELWRD
• WORDS
• CHARS
• CHAR
• SETCHAR
f o o
b a z
b a r
line
line
word word
word
Sequence Interface
• Functions
• DELLINE
• LINES
• DELWRD
• WORDS
• CHARS
• CHAR
• SETCHAR
f o o
b a z
b a r
pointer
pointer
pointer pointer
pointer
Sequence Interface
• Functions
• DELLINE
• LINES
• DELWRD
• WORDS
• CHARS
• CHAR
• SETCHAR
Sequence Interface
• Functions
• DELLINE
• LINES
• DELWRD
• WORDS
• CHARS
• CHAR
• SETCHAR
• Input: a sequence of lines
• Line: a sequence of words
• Word: a sequence of characters


sequence<T> ?
Push vs Pull
Algorithm 1
Target
Algorithm 2
Target
Algorithm 3
Target
Algorithm 1 Algorithm 2 Algorithm 3 Target
Push vs Pull
control
Output
Alphabetizer
Circular
shifter
Input
Line Storage
Decomposition 2
Decomposition 2
Push vs Pull
control
Output
Alphabetizer
Circular
shifter
Input
Line Storage
algorithm
target
Idiomatic Decomposition
• Based on hierarchical abstract interfaces
• Consistent sequence interface
• Pull based
• Efficient
• D’s ranges and algorithms
Idiomatic Decomposition
control
Output
Alphabetizer
Circular
shifter
Input
Idiomatic Decomposition
control
Output
Alphabetizer
Circular
shifter
Input
Idiomatic Decomposition
control
Output
Alphabetizer
Circular
shifter
Input
Idiomatic Decomposition
control
Output
Alphabetizer
Circular
shifter
Input
Range
Interface
sequence<T>
ID - Input
.alphabetized // alphabetizer
.each!writeln; // output
}
// --
private:
/// Performs "foo bar n baz" -> [["foo", "bar"], ["baz"]]
auto asWordLists(Range)(Range range)
{
return range
.lineSplitter
.map!(line => line
.splitter!(chr => chr.isWhite)
.filter!(word => !word.empty));
}
/// Performs [["foo", "bar"], ["baz"]] -> [["foo", "bar"], ["
auto withCircularShifts(Range)(Range range)
{
ID - Circular Shift
{
return range
.lineSplitter
.map!(line => line
.splitter!(chr => chr.isWhite)
.filter!(word => !word.empty));
}
/// Performs [["foo", "bar"], ["baz"]] ->
/// [["foo", "bar"], ["bar", "foo"], ["baz"]]
auto withCircularShifts(Range)(Range range)
{
return range
.map!(line => line.rotations)
.joiner;
}
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["
auto rotations(Range)(Range range)
ID - Circular Shift
/// Performs [["foo", "bar"], ["baz"]] ->
/// [["foo", "bar"], ["bar", "foo"], ["baz"]]
auto withCircularShifts(Range)(Range range)
{
return range
.map!(line => line.rotations)
.joiner;
}
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]]
auto rotations(Range)(Range range)
{
auto len = range.walkLength;
return range
.repeat(len)
.enumerate
.map!(item => item.value.cycle.drop(item.index).take(len));
}
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]]
auto rotations(Range)(Range range)
{
auto len = range.walkLength;
return range
.repeat(len)
.enumerate
.map!(item => item.value.cycle.drop(item.index).take(len));
}
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]]
auto rotations(Range)(Range range)
{
auto len = range.walkLength;
return range
.repeat(len)
.enumerate
.map!(item => item.value.cycle.drop(item.index).take(len));
}
f o o b a r
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]]
auto rotations(Range)(Range range)
{
auto len = range.walkLength;
return range
.repeat(len)
.enumerate
.map!(item => item.value.cycle.drop(item.index).take(len));
}
f o o b a r
f o o b a r f o o b a r
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]]
auto rotations(Range)(Range range)
{
auto len = range.walkLength;
return range
.repeat(len)
.enumerate
.map!(item => item.value.cycle.drop(item.index).take(len));
}
f o o b a r
f o o b a r f o o b a r
0 1
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]]
auto rotations(Range)(Range range)
{
auto len = range.walkLength;
return range
.repeat(len)
.enumerate
.map!(item => item.value.cycle.drop(item.index).take(len));
}
f o o b a r
f o o b a r f o o b a r
0 1
…f o o b a r f o o b a r f o o b a r …f o o b a r f o o b a r f o o b a r
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]]
auto rotations(Range)(Range range)
{
auto len = range.walkLength;
return range
.repeat(len)
.enumerate
.map!(item => item.value.cycle.drop(item.index).take(len));
}
f o o b a r
f o o b a r f o o b a r
0 1
…f o o b a r f o o b a r f o o b a r …f o o b a r f o o b a r f o o b a r
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]]
auto rotations(Range)(Range range)
{
auto len = range.walkLength;
return range
.repeat(len)
.enumerate
.map!(item => item.value.cycle.drop(item.index).take(len));
}
f o o b a r
f o o b a r f o o b a r
0 1
…f o o b a r f o o b a r f o o b a r …b a r f o o b a r f o o b a r
f o o b a r b a r f o o
ID - Alphabetizing
/// Performs [["foo", "bar"], ["baz"]] -> ["baz", "foo bar"]
auto alphabetized(Range)(Range range)
{
return range
.map!(line => line.joiner(" "))
.array
.sort!((a, b) => icmp(a, b) < 0);
}
ID - Output
/// Performs [["foo", "bar"], ["baz"]] -> ["f
auto alphabetized(Range)(Range range)
{
return range
.map!(line => line.joiner(" "))
.array
.sort!((a, b) => a.icmp(b) < 0);
}
void print(Range)(Range range)
{
range.each!writeln;
}
ID - Control
import std.range;
import std.stdio;
import std.string;
import std.uni;
public:
void run(string inputFile)
{ // Original module:
readText(inputFile) // input
.asWordLists // input
.withCircularShifts // circular shifter
.alphabetized // alphabetizer
.each!writeln; // output
}
// --
private:
void run(string inputFile)
{ // Original module:
readText(inputFile) // input
.asWordLists // input
.withCircularShifts // circular shifter
.alphabetized // alphabetizer
.each!writeln; // output
}
/// Performs "foo bar n baz" -> [["foo", "bar"], ["baz"]]
auto asWordLists(Range)(Range range)
{
return range
.lineSplitter
.map!(line => line
.splitter!isWhite
.filter!(word => !word.empty));
}
/// Performs [["foo", "bar"], ["baz"]] -> [["foo", "bar"], ["bar", "foo"], ["baz"]]
auto withCircularShifts(Range)(Range range)
{
return range
.map!(line => line.rotations)
.joiner;
}
/// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]]
auto rotations(Range)(Range range)
{
auto len = range.walkLength;
return range
.repeat(len)
.enumerate
.map!(item => item.value.cycle.drop(item.index).take(len));
}
/// Performs [["foo", "bar"], ["baz"]] -> ["baz", "foo bar"]
auto alphabetized(Range)(Range range)
{
return range
.map!(line => line.joiner(" "))
.array
.sort!((a, b) => icmp(a, b) < 0);
}
ID - Result
A Portrait of The Artist As a Young Man
a Young Man A Portrait of The Artist As
and The Sea The Old Man
Artist As a Young Man A Portrait of The
As a Young Man A Portrait of The Artist
Ascent of Man The
Descent of Man
Man A Portrait of The Artist As a Young
Man and The Sea The Old
Man Descent of
Man The Ascent of
of Man Descent
of Man The Ascent
of The Artist As a Young Man A Portrait
Old Man and The Sea The
Portrait of The Artist As a Young Man A
Sea The Old Man and The
The Artist As a Young Man A Portrait of
The Ascent of Man
The Old Man and The Sea
The Sea The Old Man and
Young Man A Portrait of The Artist As a
Conclusion
• The idiomatic D decomposition naturally reflects
the problem statement
• The decomposition that was begging to come out
• What Parnas would have done?
Conclusion
• So, what does Parnas72 mean for D?
• D has strong modelling power. What was otherwise
complex become, simple, straightforward, even
obvious.

Contenu connexe

Dernier

AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
shinachiaurasa2
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
VishalKumarJha10
 

Dernier (20)

SHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationSHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions Presentation
 
%in Durban+277-882-255-28 abortion pills for sale in Durban
%in Durban+277-882-255-28 abortion pills for sale in Durban%in Durban+277-882-255-28 abortion pills for sale in Durban
%in Durban+277-882-255-28 abortion pills for sale in Durban
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
%in Hazyview+277-882-255-28 abortion pills for sale in Hazyview
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
%in Lydenburg+277-882-255-28 abortion pills for sale in Lydenburg
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdfPayment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
Payment Gateway Testing Simplified_ A Step-by-Step Guide for Beginners.pdf
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
%in Stilfontein+277-882-255-28 abortion pills for sale in Stilfontein
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
The title is not connected to what is inside
The title is not connected to what is insideThe title is not connected to what is inside
The title is not connected to what is inside
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 

En vedette

How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
ThinkNow
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
 

En vedette (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

DConf 2016: What Parnas72 Means for D by Luis Marques

  • 1. What Parnas72
 Means for D Luís Marques, DConf 2016, May 4th 2016
 luis@luismarques.eu
  • 2. Reproducing the Classics • My experience growing artificial societies
  • 5. Growing Artificial Societies • Sugarscape SettingsSettings World speed: Agents: World resources: Minimum vision: Maximum vision: Resource types: Start Sex Death Sickness Sharing WikipedianTrade Metabolism: Grow rate: Spacing: Agent resources:
  • 6. Growing Artificial Societies • Sugarscape Animation B-3. WealthHistogramEvolutionunderRules({GI), {M, R[60,IOO]}) from aRandomInitial Distributionof Agents 4 39 39 12 15 18 21 24 27 : I ) 7 3 11 22 33 44 55 66 77 88 99 110 0 20 40 60 80 100 120 140 160 la : J 200 1:1)-.122
  • 7. What Parnas72 Means for D • Who’s Parnas? • What’s Parnas72?
  • 8. • Electrical engineer • PhD student of Alan Perlis • Applies traditional engineering principles to software design • Critic of SDI • Known for introducing the concept of “Information hiding” David Lorge Parnas
  • 9. Parnas72 On the Criteria To Be Used in Decomposing Systems into Modules Programming R. Morris Techniques Editor On the Criteria To Be Used in Decomposing Systems into Modules D.L. Parnas Carnegie-Mellon University This paper discusses modularization as a mechanism for improving the flexibility and comprehensibility of a system while allowing the shortening of its development time. The effectiveness of a "modularization" is dependent upon the criteria used in dividing the system into modules. A system design problem is presented and both a conventional and unconventional decomposition are described. It is shown that the unconventional decompositions have distinct advantages for the goals outlined. The criteria used in arriving at the decom- positions are discussed. The unconventional decomposi- tion, if implemented with the conventional assumption that a module consists of one or more subroutines, will be less efficient in most cases. An alternative approach to implementation which does not have this effect is sketched. Key Words and Phrases: software, modules, modularity, software engineering, KWIC index, software design CR Categories: 4.0 Introduction A lucid statement of the philosophy of modular programming can be found in a 1970 textbook on the design of system programs by Gouthier and Pont [1, ¶I0.23], which we quote below: 1 A well-defined segmentation of the project effort ensures system modularity. Each task forms a separate, distinct program module. At implementation time each module and its inputs and outputs are well-defined, there is no confusion in the intended interface with other system modules. At checkout time the in- tegrity of the module is tested independently; there are few sche- duling problems in synchronizing the completion of several tasks before checkout can begin. Finally, the system is maintained in modular fashion; system errors and deficiencies can be traced to specific system modules, thus limiting the scope of detailed error searching. Usually nothing is said about the criteria to be used in dividing the system into modules. This paper will discuss that issue and, by means of examples, suggest some criteria which can be used in decomposing a system into modules. Copyright @ 1972,Association for Computing Machinery, Inc. General permission to republish, but not for profit, all or part of this material is granted, provided that reference is made to this publication, to its date of issue, and to the fact that reprinting privileges were granted by permission of the Association for Com- puting Machinery. Author's address: Department of Computer Science, Carnegie- Mellon University, Pittsburgh, PA 15213. 1053 A Brief Status Report The major advancement in the area of modular programming has been the development of coding techniques and assemblers which (l) allow one module to be written with little knowledge of the code in another module, and (2) allow modules to be reas- sembled and replaced without reassembly of the whole system. This facility is extremely valuable for the production of large pieces of code, but the systems most often used as examples of problem systems are highly- modularized programs and make use of the techniques mentioned above. 1Reprinted by permission of Prentice-Hall, Englewood Cliffs, N.J. Communications December 1972 of Volume 15 the ACM Number 12
  • 10. Parnas72(b) • Popularizes information-hiding modules • See Parnas72a: “Information Distribution Aspects of Design Methodology" • Topic: • Architecture? Well… • Work assignments! • Documentation! • (Parnas72a)
  • 11. Parnas72(b) • The documentation story • Philips Computer Industry • A manager asked for help on creating work assignments • How to create specifications so that modules would integrate successfully • Difficult because the modules had to know a lot about each other • How to properly decompose into modules?
  • 12. Modularization • A module is a work/responsibility assignment • A class, a D module, a component, etc. • “The modularizations include the design decisions which must be made before the work on independent modules can begin” • “Architecture is the set of design decisions that must be made early in a project” (Fowler, “Who Needs an Architect?”, 2003)
  • 13. Example • KWIC Index program • First, two Parnas modularizations • Then, an idiomatic D modularization
  • 14. KWIC • “The KWIC index system accepts an ordered set of lines, each line is an ordered set of words, and each word is an ordered set of characters. Any line may be “circularly shifted” by repeatedly removing the first word and appending it at the end of the line. The KWIC index system outputs a listing of all circular shifts of all the lines in alphabetical order”
  • 16. KWIC Descent of Man The Ascent of Man The Old Man and The Sea A Portrait of The Artist As a Young Man (Key Word In Context) https://users.cs.duke.edu/~ola/ipc/kwic.html
  • 17. KWIC a portrait of the ARTIST as a young man the ASCENT of man DESCENT of man a portrait of the artist as a young MAN descent of MAN the ascent of MAN the old MAN and the sea the OLD man and the sea a PORTRAIT of the artist as a young man the old man and the SEA a portrait of the artist as a YOUNG man
  • 18. KWIC a portrait of the ARTIST as a young man the ASCENT of man DESCENT of man a portrait of the artist as a young MAN descent of MAN the ascent of MAN the old MAN and the sea the OLD man and the sea a PORTRAIT of the artist as a young man the old man and the SEA a portrait of the artist as a YOUNG man
  • 19. KWIC a portrait of the ARTIST as a young man the ASCENT of man DESCENT of man a portrait of the artist as a young MAN descent of MAN the ascent of MAN the old MAN and the sea the OLD man and the sea a PORTRAIT of the artist as a young man the old man and the SEA a portrait of the artist as a YOUNG man
  • 20. KWIC A Portrait of The Artist As a Young Man The Ascent of Man Descent of Man A Portrait of The Artist As a Young Man Descent of Man The Ascent of Man The Old Man and The Sea The Old Man and The Sea A Portrait of The Artist As a Young Man The Old Man and The Sea A Portrait of The Artist As a Young Man
  • 21. KWIC Artist As a Young Man, A Portrait of The Ascent of Man, The Descent of Man Man, A Portrait of The Artist As a Young Man, Descent of Man, The Ascent of Man and The Sea, The Old Old Man and The Sea, The Portrait of The Artist As a Young Man, A Sea, The Old Man and The Young Man, A Portrait of The Artist As a
  • 22. KWIC Artist As a Young Man, A Portrait of The Ascent of Man, The Descent of Man Man, A Portrait of The Artist As a Young Man, Descent of Man, The Ascent of Man and The Sea, The Old Old Man and The Sea, The Portrait of The Artist As a Young Man, A Sea, The Old Man and The Young Man, A Portrait of The Artist As a
  • 23. KWIC Artist As a Young Man A Portrait of The Ascent of Man The Descent of Man Man A Portrait of The Artist As a Young Man Descent of Man The Ascent of Man and The Sea The Old Old Man and The Sea The Portrait of The Artist As a Young Man A Sea The Old Man and The Young Man A Portrait of The Artist As a
  • 24. KWIC Artist As a Young Man A Portrait of The Ascent of Man The Descent of Man Man A Portrait of The Artist As a Young Man and The Sea The Old Man Descent of Man The Ascent of Old Man and The Sea The Portrait of The Artist As a Young Man A Sea The Old Man and The Young Man A Portrait of The Artist As a
  • 25. KWIC A Portrait of The Artist As a Young Man a Young Man A Portrait of The Artist As and The Sea The Old Man Artist As a Young Man A Portrait of The As a Young Man A Portrait of The Artist Ascent of Man The Descent of Man Man A Portrait of The Artist As a Young Man and The Sea The Old Man Descent of Man The Ascent of of Man Descent of Man The Ascent of The Artist As a Young Man A Portrait Old Man and The Sea The Portrait of The Artist As a Young Man A Sea The Old Man and The The Artist As a Young Man A Portrait of The Ascent of Man The Old Man and The Sea The Sea The Old Man and Young Man A Portrait of The Artist As a
  • 26. KWIC A Portrait of The Artist As a Young Man a Young Man A Portrait of The Artist As and The Sea The Old Man Artist As a Young Man A Portrait of The As a Young Man A Portrait of The Artist Ascent of Man The Descent of Man Man A Portrait of The Artist As a Young Man and The Sea The Old Man Descent of Man The Ascent of of Man Descent of Man The Ascent of The Artist As a Young Man A Portrait Old Man and The Sea The Portrait of The Artist As a Young Man A Sea The Old Man and The The Artist As a Young Man A Portrait of The Ascent of Man The Old Man and The Sea The Sea The Old Man and Young Man A Portrait of The Artist As a
  • 27. KWIC • Input: a sequence of lines • Line: a sequence of words • Word: a sequence of characters • Circular shift: • foo bar baz → baz foo bar • Output: all circular shifts of all lines, in alphabetical order
  • 28. A Tale of 2 Decompositions • Parnas’ two decompositions reimplemented in D • <https://github.com/luismarques/parnas72>
  • 29. Decomposition 1 (D1) • Idea: • The flowchart method • The classic method • Data-oriented design • Module == collection of procedures
  • 30. Decomposition 1 (D1) canonical representation of input all circular shifts of input lines
 (no order requirements) all circular shifts of input lines
 in alphabetical order pretty printed index control Output Alphabetizer Circular shifter Input input data
  • 31. D1 - Input • Module 1: Input. This module reads the data lines from the input medium and stores them in core for processing by the remaining modules. The characters are packed four to a word, and an otherwise unused character is used to indicate the end of a word. An index is kept to show the starting address of each line.
  • 32. D1 - Input Foo import std.utf; public: // change these definitions, for different performance alias LineNum = ptrdiff_t; alias WordNum = ptrdiff_t; alias CharNum = ptrdiff_t; enum wordSeparator = ' '; string data; CharNum[] lineIndex; // Reads the lines from the input medium, and stores e // `wordSeparator` character constant. The Lines are s // and a separate index of where the lines start is ke void readLines(string filename) { F o o ␣ D 0 0 0 F o o ␣ D L u í s { 1 char / 1 dchar { 2 char / 1 dchar •••• •••• • •••• ••••
  • 33. D1 - Input • Input: Descent␣of␣Man↵The⇥Ascent⇥␣of␣Man␣␣↵The␣ Old␣Man␣and␣The␣Sea↵␣␣↵A␣Portrait␣of␣The␣Ar tist␣As␣a␣Young␣Man • Output: Descent␣of␣ManThe␣Ascent␣of␣ManThe␣Old␣Man␣ and␣The␣SeaA␣Portrait␣of␣The␣Artist␣As␣a␣You ng␣Man
 [0, 14, 31, 54]
  • 34. // change these definitions, for different performance/capability trade-offs: alias LineNum = ptrdiff_t; alias WordNum = ptrdiff_t; alias CharNum = ptrdiff_t; enum wordSeparator = ' '; string data; CharNum[] lineIndex; // Reads the lines from the input medium, and stores each word separated by a // `wordSeparator` character constant. The Lines are stored without a separator, // and a separate index of where the lines start is kept in `lineIndex`. void readLines(string filename) { size_t lineStart; data = readText(filename) .lineSplitter // normalize the spacing between words .map!(line => line .splitter!isWhite .filter!(c => !c.empty) .joiner([wordSeparator])) // remove empty lines .filter!(line => !line.empty) // keep an index of where each line starts .tee!((line) { lineIndex ~= lineStart; lineStart += line.byChar.walkLength; }) // join the lines .joiner .to!string; } D1 - Input
  • 35. // Reads the lines from the input medium, and stores each word separated by a // `wordSeparator` character constant. The Lines are stored without a separator, // and a separate index of where the lines start is kept in `lineIndex`. void readLines(string filename) { size_t lineStart; data = readText(filename) .lineSplitter // normalize the spacing between words .map!(line => line .splitter!isWhite .filter!(c => !c.empty) .joiner([wordSeparator])) // remove empty lines .filter!(line => !line.empty) // keep an index of where each line starts .tee!((line) { lineIndex ~= lineStart; lineStart += line.byChar.walkLength; }) // join the lines .joiner .to!string; }
  • 36. data = readText(filename) .lineSplitter // normalize the spacing between words .map!(line => line .splitter!isWhite .filter!(c => !c.empty) .joiner([wordSeparator])) // remove empty lines .filter!(line => !line.empty) // keep an index of where each line starts .tee!((line) { lineIndex ~= lineStart; lineStart += line.byChar.walkLength; }) Descent␣of␣Man↵The⇥Ascent⇥␣of␣Man␣␣↵The␣Old␣M an␣and␣The␣Sea↵␣␣↵A␣Portrait␣of␣The␣Artist␣As␣a␣ Young␣Man 
 [Descent␣of␣Man][The⇥Ascent⇥␣of␣Man␣␣] [The␣Old␣Man␣and␣The␣Sea][␣␣] [A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man]
  • 37. // normalize the spacing between words .map!(line => line .splitter!isWhite .filter!(c => !c.empty) .joiner([wordSeparator])) // remove empty lines .filter!(line => !line.empty) // keep an index of where each line starts .tee!((line) { lineIndex ~= lineStart; lineStart += line.byChar.walkLength; }) // join the lines .joiner .to!string; [Descent␣of␣Man][The⇥Ascent⇥␣of␣Man␣␣] [The␣Old␣Man␣and␣The␣Sea][␣␣] [A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man] [[Descent][of][Man]][[The][Ascent][][of] [Man][][]][[The][Old][Man][and][The][Sea]] [[][][]][[A][Portrait][of][The][Artist] [As][a][Young][Man]]
  • 38. // normalize the spacing between words .map!(line => line .splitter!isWhite .filter!(c => !c.empty) .joiner([wordSeparator])) // remove empty lines .filter!(line => !line.empty) // keep an index of where each line starts .tee!((line) { lineIndex ~= lineStart; lineStart += line.byChar.walkLength; }) // join the lines .joiner .to!string; [[Descent][of][Man]][[The][Ascent][][of] [Man][][]][[The][Old][Man][and][The][Sea]] [[][][]][[A][Portrait][of][The][Artist] [As][a][Young][Man]] [[Descent][of][Man]][[The][Ascent][of] [Man]][[The][Old][Man][and][The][Sea]][] [[A][Portrait][of][The][Artist][As][a] [Young][Man]]
  • 39. // normalize the spacing between words .map!(line => line .splitter!isWhite .filter!(c => !c.empty) .joiner([wordSeparator])) // remove empty lines .filter!(line => !line.empty) // keep an index of where each line starts .tee!((line) { lineIndex ~= lineStart; lineStart += line.byChar.walkLength; }) // join the lines .joiner .to!string; [[Descent][of][Man]][[The][Ascent][of] [Man]][[The][Old][Man][and][The][Sea]][] [[A][Portrait][of][The][Artist][As][a] [Young][Man]] [Descent␣of␣Man][The␣Ascent␣of␣Man] [The␣Old␣Man␣and␣The␣Sea][] [A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man]
  • 40. // remove empty lines .filter!(line => !line.empty) // keep an index of where each line starts .tee!((line) { lineIndex ~= lineStart; lineStart += line.byChar.walkLength; }) // join the lines .joiner .to!string; } [Descent␣of␣Man][The␣Ascent␣of␣Man] [The␣Old␣Man␣and␣The␣Sea][] [A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man] 
 [Descent␣of␣Man][The␣Ascent␣of␣Man] [The␣Old␣Man␣and␣The␣Sea] [A␣Portrait␣of␣The␣Artist␣As␣a␣Young␣Man]
  • 41. // remove empty lines .filter!(line => !line.empty) // keep an index of where each line starts .tee!((line) { lineIndex ~= lineStart; lineStart += line.byChar.walkLength; }) // join the lines .joiner .to!string; } Descent␣of␣ManThe␣Ascent␣of␣ManThe␣Old␣Man␣ and␣The␣SeaA␣Portrait␣of␣The␣Artist␣As␣a␣You ng␣Man
  • 42. D1 - Circular Shift • Module 2: Circular Shift. This module is called after the input module has completed its work. It prepares an index which gives the address of the first character of each circular shift, and the original index of the line in the array made up by module 1. It leaves its output in core with words in pairs (original line number, starting address).
  • 43. D1 - Circular Shift import std.algorithm; import std.range; import std.typecons; import std.utf; import one.input; public: struct ShiftIndexEntry { LineNum lineNum; CharNum firstChar; } ShiftIndexEntry[] shiftIndex; void setup() { shiftIndex = iota(lineIndex.length) // 0
  • 44. D1 - Circular Shift Circular shifter Input .map!(a => a.value.byCodeUnit .enumerate // (0, ((0, char1), (1, char2 .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index] .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; } private: auto line(LineNum lineNum) { auto lineStart = lineIndex[lineNum]; auto lineEnd = lineNum+1 >= lineIndex.length ? data.length : lineIndex[lineNum+1]; return data[lineStart .. lineEnd]; }
  • 45. D1 - Circular Shift { LineNum lineNum; CharNum firstChar; } ShiftIndexEntry[] shiftIndex; void setup() { shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; } private:
  • 46. shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum)
  • 47. shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum) 0, 1, … [Descent␣of␣Man]
 [The␣Ascent␣of␣Man] …
  • 48. shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum) (0, [Descent␣of␣Man])
 (1, [The␣Ascent␣of␣Man]) [Descent␣of␣Man]
 [The␣Ascent␣of␣Man]
  • 49. shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum) [0, 8, 11]
 [14, 18, 25, 28] (0, [Descent␣of␣Man])
 (1, [The␣Ascent␣of␣Man])
  • 50. shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum) (0, [Descent␣of␣Man])
 (1, [The␣Ascent␣of␣Man]) 0 1 2 3 4 5 6 7 8 9 10 11 12 13
  • 51. (0, [[Descent][of] (1, [[The][Ascent] shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum) 0 1 2 3 4 5 6 8 9 (0, [Descent␣of␣Man])
 (1, [The␣Ascent␣of␣Man]) 0 1 2 3 4 5 6 7 8 9 10 11 12 13
  • 52. (0, [[Descent][of][Man]])
 (1, [[The][Ascent][of][Man]]) shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum) 0 1 2 3 4 5 6 8 9 11 13 [0, 8, 11]
 [14, 18, 25, 28]
  • 53. (0, [0, 8, 11]) (1, [14, 18, 25, 28]) [0, 8, 11]
 [14, 18, 25, 28] shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum)
  • 54. shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum) (0, [0, 8, 11]) (1, [14, 18, 25, 28]) [ ShiftIndexEntry(0, 0),
 ShiftIndexEntry(0, 8), 
 ShiftIndexEntry(0, 11) ] [ ShiftIndexEntry(1, 14), ShiftIndexEntry(1, 18), ShiftIndexEntry(1, 25), ShiftIndexEntry(1, 28) ]
  • 55. shiftIndex = iota(lineIndex.length) .map!(lineNum => line(lineNum)) .enumerate .map!(a => a.value.byCodeUnit .enumerate .splitter!(b => b.value == wordSeparator) .map!(b => b.front.index + lineIndex[a.index])) .enumerate .map!(a => a.value .map!(b => ShiftIndexEntry(a.index, b))) .joiner .array; vate: o line(LineNum lineNum) ShiftIndexEntry(0, 0)
 ShiftIndexEntry(0, 8) 
 ShiftIndexEntry(0, 11) ShiftIndexEntry(1, 14) ShiftIndexEntry(1, 18) ShiftIndexEntry(1, 25) ShiftIndexEntry(1, 28)
  • 56. D1 - Alphabetizing • Module 3: Alphabetizing. This module takes as input the arrays produced by modules 1 and 2. It produces an array in the same format as that produced by module 2. In this case, however, the circular shifts are listed in another order (alphabetically).
  • 57. D1 - Alphabetizing import std.typecons; import std.utf; import one.input; public: struct ShiftIndexEntry { LineNum lineNum; CharNum firstChar; } ShiftIndexEntry[] shiftIndex; void setup() { shiftIndex = iota(lineIndex.length) // 0, 1, 2 ... .map!(lineNum => line(lineNum)) // first line, se .enumerate // (0, first line), (1, secon .map!(a => a.value.byCodeUnit [The␣Ascent␣of␣Man] [Ascent␣of␣Man␣The] [Ascent␣of␣Man][The] [The␣Ascent␣of␣Man] [Ascent␣of␣Man␣The]
  • 58. [Ascent␣of␣Man][The] [The␣Ascent␣of␣Man] [Ascent␣of␣Man␣The] private: auto line(ShiftIndexEntry entry) { auto a = entry.firstChar; auto b = entry.lineNum+1 >= lineIndex.length ? data.length : lineIndex[entry.lineNum+1]; auto c = lineIndex[entry.lineNum]; auto d = (entry.firstChar-1).max(0).max(c); auto x = data[a .. b]; auto y = data[c .. d]; return joiner(only(x, y).filter!(a => !a.empty), " "); } c d a b y x
  • 59. D1 - Alphabetizing module one.alphabetizer; import std.algorithm; import std.math; import std.range; import std.typecons; import std.uni; import std.utf; import one.input; import one.circularshifter; public: void setup() { shiftIndex.sort!((a, b) => icmp(line(a), line(b)) < 0); } private: auto line(ShiftIndexEntry entry) { auto a = entry.firstChar; auto b = entry.lineNum+1 >= lineIndex.length ? data.length : lineIndex[entry.lineNum+1];
  • 60. D1 - Output • Module 4: Output. Using the arrays produced by module 3 and module 1, this modules produces a nicely formatted output listing all of the circular shifts. In a sophisticated system the actual start of each line will be marked, pointers to further information may be inserted, and the start of the circular shift may actually not be the first word in the line, etc.
  • 61. D1 - Output void printLines() { shiftIndex.map!(entry => entry.line).each!writeln; } private: auto line(ShiftIndexEntry entry) { auto a = entry.firstChar; auto b = entry.lineNum+1 >= lineIndex.length ? data.length : lineIndex[entry.lineNum+1]; auto c = lineIndex[entry.lineNum]; auto d = (entry.firstChar-1).max(0).max(c); auto x = data[a .. b]; auto y = data[c .. d]; return joiner(only(x, y).filter!(a => !a.empty), " "); }
  • 62. D1 - Outputmodule one.output; import std.algorithm; import std.range; import std.stdio; import one.input; import one.circularshifter; void printLines() { shiftIndex.map!(entry => entry.line).each!writeln; } private: auto line(ShiftIndexEntry entry) { auto a = entry.firstChar; auto b = entry.lineNum+1 >= lineIndex.length ? data.length : lineIndex[entry.lineNum+1];
  • 63. D1 - Master Control • Module 5: Master Control. This module does little more than control the sequencing among the other four modules. It may also handle error messages, space allocation, etc.
  • 64. D1 - Master Control module one.control; import one.input; import one.circularshifter; import one.alphabetizer; import one.output; public: void run(string inputFile) { readLines(inputFile); one.circularshifter.setup(); one.alphabetizer.setup(); printLines(); }
  • 65. D1 - Result A Portrait of The Artist As a Young Man a Young Man A Portrait of The Artist As and The Sea The Old Man Artist As a Young Man A Portrait of The As a Young Man A Portrait of The Artist Ascent of Man The Descent of Man Man A Portrait of The Artist As a Young Man and The Sea The Old Man Descent of Man The Ascent of of Man Descent of Man The Ascent of The Artist As a Young Man A Portrait Old Man and The Sea The Portrait of The Artist As a Young Man A Sea The Old Man and The The Artist As a Young Man A Portrait of The Ascent of Man The Old Man and The Sea The Sea The Old Man and Young Man A Portrait of The Artist As a
  • 66. Decomposition 2 (D2) • Based on: • Difficult design decisions, or design decisions which are likely to change (the secret) • Modules are designed to hide these decisions from the others • Abstract interface • Efficient work partition
  • 68. D2 - Line Storage • Module 1: Line Storage. This module consists of a number of functions(…). • WORD • SETWRD • WORDS • LINES • DELWRD • DELLINE • CHARS
  • 69. D2 - Line Storage Definition of a "Line Storage" Module Introduction: This definition specifies a mechanism which hold up to pi lines, each line consisting of up to p2 word may be up to p3 characters. Function WORD possible values: integers initial values: undefined parameters: l»w,c all effect: j . call call call call call call ERLWEL ERLWNL ERLWEW ERLWNW ERLWEC ERLWNC integer if if if if if if 1 < 1 > w < w > c < c > 1 or 1 > LINES 1 or w > WORDS(1) 1 or c > CHARS(l,w) P2 P3 Function SETWRD possible values: initial values: none not applicable
  • 70. D2 - Line Storage call call call call call call ERLWEL ERLWNL ERLWEW ERLWNW ERLWEC ERLWNC if if if if if if 1 < 1 > w < w > c < c > 1 or 1 > LINES 1 or w > WORDS(1) 1 or c > CHARS(l,w) P2 P3 Function SETWRD possible values: initial values: parameters: effect: none not applicable l,w,c,d all integers call call call call call call call call if 1 if w ERLSLE ERLSBL ERLSBL ERLSWE ERLSBW ERLSBW ERLSCE ERLSBC = 'LINES' 1 or 1 > pl 'LINES' +1 'LINES' 1 or w > p2 'WORDS'(1) + 1 'WORDS'(1) 1 or c > p3 if c .noteq. 'CHARS'(l,w)+l +1 then LINES = 'LINES* + 1 if 1 < if 1 > if 1 < if w < if w > if w < if c < = 'WORDS*(1) +1 then WORDS(1) CHARS(l,w) = c WORDQ,w,c) = d Function WORDS
  • 71. D2 - Line Storage Definition of a "Line Storage" Module Introduction: This definition specifies a mechanism which hold up to pi lines, each line consisting of up to p2 word may be up to p3 characters. Function WORD possible values: integers initial values: undefined parameters: l»w,c all effect: j . call call call call call call ERLWEL ERLWNL ERLWEW ERLWNW ERLWEC ERLWNC integer if if if if if if 1 < 1 > w < w > c < c > 1 or 1 > LINES 1 or w > WORDS(1) 1 or c > CHARS(l,w) P2 P3 Function SETWRD possible values: initial values: none not applicable CHAR r
  • 72. D2 - Line Storage • The function call CHAR(r,w,c) will have as value an integer representing the cth character in the rth line, wth word • A call such as SETCHAR(r,w,c,d) will cause the cth character in the wth word of the rth line to be the character represented by d (i.e., CHAR(r,w,c) = d) • WORDS(r) returns the number of words in line r • Etc.
  • 73. D2 - Line Storage • Functions • CHAR • SETCHAR • WORDS • LINES • DELWRD • DELLINE • CHARS
  • 74. D2 - Line Storage module two.linestorage; import std.algorithm; import std.range; import std.utf; public: // change these definitions, for different performance/capabili alias LineNum = ptrdiff_t; alias WordNum = ptrdiff_t; alias CharNum = ptrdiff_t; enum maxLines = LineNum.max; /// (original name: p1) enum maxWordsPerLine = WordNum.max; /// (original name: p2) enum maxCharsPerWord = CharNum.max; /// (original name: p3) /// Returns one character from a given word, present at a given /// (original name: WORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum
  • 75. D2 - Line Storage void removeLine(LineNum lineNum) { assert(lineNum >= 0 && lineNum < numLine assert(false, "not implemented"); } // -- private: enum wordSeparator = ' '; char[] data; CharNum[] lineIndex; auto line(LineNum lineNum) { auto lineStart = lineIndex[lineNum]; auto lineEnd = lineNum+1 >= lineIndex.le private:
  • 76. D2 - Line Storage // -- private: enum wordSeparator = ' '; char[] data; CharNum[] lineIndex; auto line(LineNum lineNum) { auto lineStart = lineIndex[lineNum]; auto lineEnd = lineNum+1 >= lineIndex.length ? data.length : lineIndex[lineNum+1]; return data[lineStart .. lineEnd].byCodeUnit; } /// Returns a range of words for a given line auto wordsForLine(LineNum lineNum) { return line(lineNum).splitter(wordSeparator); } private:
  • 77. D2 - Line Storage alias CharNum = ptrdiff_t; enum maxLines = LineNum.max; /// (original name: p1) enum maxWordsPerLine = WordNum.max; /// (original name: p2) enum maxCharsPerWord = CharNum.max; /// (original name: p3) /// Returns one character from a given word, from line `lineNum`. /// (original name: WORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum) { assert(lineNum >= 0 && lineNum < maxLines); assert(lineNum < numLines); assert(wordNum >= 0 && wordNum < maxWordsPerLine); assert(wordNum < numWords(lineNum)); assert(charNum >= 0 && charNum < maxCharsPerWord); assert(charNum < numCharacters(lineNum, wordNum)); return wordsForLine(lineNum) .dropExactly(wordNum) .front .dropExactly(charNum) .front; }
  • 78. /// Returns one character from a given word, from line `lineNum`. /// (original name: WORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum) { assert(lineNum >= 0 && lineNum < maxLines); assert(lineNum < numLines); assert(wordNum >= 0 && wordNum < maxWordsPerLine); assert(wordNum < numWords(lineNum)); assert(charNum >= 0 && charNum < maxCharsPerWord); assert(charNum < numCharacters(lineNum, wordNum)); return wordsForLine(lineNum) .dropExactly(wordNum) .front .dropExactly(charNum) .front; } /// Sets the next character for the current or the next word. /// The current word is the last word present at the last line. /// (original name: SETWRD) void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch { assert(lineNum >= 0 && lineNum < maxLines);
  • 79. /// Returns one character from a given word, from line `lineNum`. /// (original name: WORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum) { assert(lineNum >= 0 && lineNum < maxLines); assert(lineNum < numLines); assert(wordNum >= 0 && wordNum < maxWordsPerLine); assert(wordNum < numWords(lineNum)); assert(charNum >= 0 && charNum < maxCharsPerWord); assert(charNum < numCharacters(lineNum, wordNum)); return wordsForLine(lineNum) .dropExactly(wordNum) .front .dropExactly(charNum) .front; } /// Sets the next character for the current or the next word. /// The current word is the last word present at the last line. /// (original name: SETWRD) void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch { assert(lineNum >= 0 && lineNum < maxLines); f o o a rb
  • 80. /// Returns one character from a given word, from line `lineNum`. /// (original name: WORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum) { assert(lineNum >= 0 && lineNum < maxLines); assert(lineNum < numLines); assert(wordNum >= 0 && wordNum < maxWordsPerLine); assert(wordNum < numWords(lineNum)); assert(charNum >= 0 && charNum < maxCharsPerWord); assert(charNum < numCharacters(lineNum, wordNum)); return wordsForLine(lineNum) .dropExactly(wordNum) .front .dropExactly(charNum) .front; } /// Sets the next character for the current or the next word. /// The current word is the last word present at the last line. /// (original name: SETWRD) void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch { assert(lineNum >= 0 && lineNum < maxLines); f o o a rb
  • 81. /// Returns one character from a given word, from line `lineNum`. /// (original name: WORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum) { assert(lineNum >= 0 && lineNum < maxLines); assert(lineNum < numLines); assert(wordNum >= 0 && wordNum < maxWordsPerLine); assert(wordNum < numWords(lineNum)); assert(charNum >= 0 && charNum < maxCharsPerWord); assert(charNum < numCharacters(lineNum, wordNum)); return wordsForLine(lineNum) .dropExactly(wordNum) .front .dropExactly(charNum) .front; } /// Sets the next character for the current or the next word. /// The current word is the last word present at the last line. /// (original name: SETWRD) void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch { assert(lineNum >= 0 && lineNum < maxLines); a rb
  • 82. /// Returns one character from a given word, from line `lineNum`. /// (original name: WORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum) { assert(lineNum >= 0 && lineNum < maxLines); assert(lineNum < numLines); assert(wordNum >= 0 && wordNum < maxWordsPerLine); assert(wordNum < numWords(lineNum)); assert(charNum >= 0 && charNum < maxCharsPerWord); assert(charNum < numCharacters(lineNum, wordNum)); return wordsForLine(lineNum) .dropExactly(wordNum) .front .dropExactly(charNum) .front; } /// Sets the next character for the current or the next word. /// The current word is the last word present at the last line. /// (original name: SETWRD) void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch { assert(lineNum >= 0 && lineNum < maxLines); a rb
  • 83. /// Returns one character from a given word, from line `lineNum`. /// (original name: WORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum) { assert(lineNum >= 0 && lineNum < maxLines); assert(lineNum < numLines); assert(wordNum >= 0 && wordNum < maxWordsPerLine); assert(wordNum < numWords(lineNum)); assert(charNum >= 0 && charNum < maxCharsPerWord); assert(charNum < numCharacters(lineNum, wordNum)); return wordsForLine(lineNum) .dropExactly(wordNum) .front .dropExactly(charNum) .front; } /// Sets the next character for the current or the next word. /// The current word is the last word present at the last line. /// (original name: SETWRD) void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, ch { assert(lineNum >= 0 && lineNum < maxLines); a r
  • 84. /// Sets the next character for the current or the next word. /// The current word is the last word present at the last line. /// (original name: SETWRD) void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, char charValue) { assert(lineNum >= 0 && lineNum < maxLines); assert(lineNum == numLines-1 || lineNum == numLines); assert(wordNum >= 0 && wordNum < maxWordsPerLine); assert(charNum >= 0 && charNum < maxCharsPerWord); if(lineNum < numLines) { auto nw = numWords(lineNum); assert(wordNum == nw-1 || wordNum == nw); if(wordNum < nw) { assert(charNum == numCharacters(lineNum, wordNum)); } } if(lineNum == numLines) { lineIndex ~= data.length; } else { if(wordNum == numWords(lineNum)) { data ~= wordSeparator; } } data ~= charValue; }
  • 85. D2 - Line Storage /// Sets the next character for the current or the next word. /// The current word is the last word present at the last line. /// (original name: SETWRD) void setWordChar(LineNum lineNum, WordNum wordNum, CharNum charNum, char charValue) { if(lineNum == numLines) { lineIndex ~= data.length; } else { if(wordNum == numWords(lineNum)) { data ~= wordSeparator; } } data ~= charValue; }
  • 86. D2 - Input • Module 2: Input. This module reads the original lines from the input media and calls the line storage module to have them stored internally.
  • 87. D2 - Inputmodule two.input; import std.stdio; import std.uni; import two.linestorage; public: void readLines(string inputFile) { foreach(lineNum, line; File(inputFile).byLine) { foreach(charNum, c; line) { setWordChar(lineNum, wordNum, charNum, c); } } }
  • 88. void readLines(string inputFile) { LineNum lineNum; foreach(line; File(inputFile).byLine) { WordNum wordNum; CharNum charNum; bool incWordNum; bool incLineNum; foreach(c; line) { if(c.isWhite) incWordNum = true; else { if(incWordNum) { wordNum++; charNum = 0; incWordNum = false; } setWordChar(lineNum, wordNum, charNum, c); charNum++; incLineNum = true; } } if(incLineNum) { lineNum++; incLineNum = false; } } }
  • 89. D2 - Circular Shifter • Module 3: Circular Shifter. The principal functions provided by this module are analogs of functions provided in module 1. (…) • CHAR → CSCHAR • WORDS → CSWORDS • LINES → CSLINES • CHARS → CSCHARS • … • A function CSSETUP is provided which must be called before the other functions have their value specified.
  • 90. D2 - Circular Shifter private: WordNum storageWordNum = (entry.firstWor storage.numWords(entry.lineNum); return storage.numCharacters(entry.lineN } // -- private: struct ShiftIndexEntry { LineNum lineNum; WordNum firstWord; } ShiftIndexEntry[] shiftIndex;
  • 91. D2 - Circular Shifter public: enum maxLines = LineNum.max; /// (original name: p4) alias LineNum = storage.LineNum; alias WordNum = storage.WordNum; alias CharNum = storage.CharNum; /// (original name: CSSTUP) void setup() { shiftIndex = iota(storage.numLines) .map!(a => storage.numWords(a).iota .map!(b => ShiftIndexEntry(a, b))) .joiner .array; } /// (original name: CSWORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNu {
  • 92. /// (original name: CSSTUP) void setup() { shiftIndex = iota(storage.numLines) .map!(a => storage.numWords(a).iota .map!(b => ShiftIndexEntry(a, b))) .joiner .array; } /// (original name: CSWORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNu { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + wordNu storage.numWords(entry.lineNum); return storage.wordChar(entry.lineNum, storageWord } /// (original name: CSWRDS)
  • 93. /// (original name: CSSTUP) void setup() { shiftIndex = iota(storage.numLines) .map!(a => storage.numWords(a).iota .map!(b => ShiftIndexEntry(a, b))) .joiner .array; } /// (original name: CSWORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNu { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + wordNu storage.numWords(entry.lineNum); return storage.wordChar(entry.lineNum, storageWord } /// (original name: CSWRDS) 0, 1, …
  • 94. /// (original name: CSSTUP) void setup() { shiftIndex = iota(storage.numLines) .map!(a => storage.numWords(a).iota .map!(b => ShiftIndexEntry(a, b))) .joiner .array; } /// (original name: CSWORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNu { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + wordNu storage.numWords(entry.lineNum); return storage.wordChar(entry.lineNum, storageWord } /// (original name: CSWRDS) 0, 1, … 3 words, 4 words, …
  • 95. /// (original name: CSSTUP) void setup() { shiftIndex = iota(storage.numLines) .map!(a => storage.numWords(a).iota .map!(b => ShiftIndexEntry(a, b))) .joiner .array; } /// (original name: CSWORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNu { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + wordNu storage.numWords(entry.lineNum); return storage.wordChar(entry.lineNum, storageWord } /// (original name: CSWRDS) 0, 1, … 3 words, 4 words, … [0, 1, 2], [0, 1, 2, 3], …
  • 96. /// (original name: CSSTUP) void setup() { shiftIndex = iota(storage.numLines) .map!(a => storage.numWords(a).iota .map!(b => ShiftIndexEntry(a, b))) .joiner .array; } /// (original name: CSWORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNu { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + wordNu storage.numWords(entry.lineNum); return storage.wordChar(entry.lineNum, storageWord } /// (original name: CSWRDS) 0, 1, … 3 words, 4 words, … [0, 1, 2], [0, 1, 2, 3], … [ShiftIndexEntry(0, 0), ShiftIndexEntry(0, 1), …] […, ShiftIndexEntry(1, 2), ShiftIndexEntry(1, 3)]
 …
  • 97. /// (original name: CSSTUP) void setup() { shiftIndex = iota(storage.numLines) .map!(a => storage.numWords(a).iota .map!(b => ShiftIndexEntry(a, b))) .joiner .array; } /// (original name: CSWORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNu { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + wordNu storage.numWords(entry.lineNum); return storage.wordChar(entry.lineNum, storageWord } /// (original name: CSWRDS) ShiftIndexEntry(0, 0) ShiftIndexEntry(0, 1) ShiftIndexEntry(0, 3) ShiftIndexEntry(1, 0) ShiftIndexEntry(1, 1) ShiftIndexEntry(1, 2) ShiftIndexEntry(1, 3) …
  • 98. D2 - Circular Shifter /// (original name: CSWRDS) LineNum numWords(LineNum lineNum) { auto entry = shiftIndex[lineNum]; return storage.numWords(entry.lineNum); } /// (original name: CSLNES) LineNum numLines() { return shiftIndex.length.to!LineNum; } /// (original name: CSCHRS) CharNum numCharacters(LineNum lineNum, WordNum wordN { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + word storage.numWords(entry.lineNum); } /// (original name: CSWORD) char wordChar(LineNum lineNum, WordNum wordNum, CharN { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + wordN storage.numWords(entry.lineNum); return storage.wordChar(entry.lineNum, storageWor } /// (original name: CSWRDS) LineNum numWords(LineNum lineNum) { auto entry = shiftIndex[lineNum]; return storage.numWords(entry.lineNum); }
  • 99. D2 - Circular Shifter void setup() { shiftIndex = iota(storage.numLines) .map!(a => storage.numWords(a).iota .map!(b => ShiftIndexEntry(a, b))) .joiner .array; } /// (original name: CSWORD) char wordChar(LineNum lineNum, WordNum wordNum, CharNum charNum) { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + wordNum) % storage.numWords(entry.lineNum); return storage.wordChar(entry.lineNum, storageWordNum, charNum); } /// (original name: CSWRDS) LineNum numWords(LineNum lineNum) { auto entry = shiftIndex[lineNum];
  • 100. D2 - Circular Shifter /// (original name: CSCHRS) CharNum numCharacters(LineNum lineNum, WordNum wordNum) { auto entry = shiftIndex[lineNum]; WordNum storageWordNum = (entry.firstWord + wordNum) % storage.numWords(entry.lineNum); return storage.numCharacters(entry.lineNum, storageWordNum); } // -- private:
  • 101. D2 - Alphabetizing • Module 4: Alphabetizing. (…) ITH(i) will give the index of the circular shift which comes ith in the alphabetical ordering.
  • 102. auto alphabeticIndex() { auto indexOffsets = new LineNum[numLines]; makeIndex!((a, b) => a.icmp(b) < 0)(numLines.iota.map!(a => l indexOffsets); return indexOffsets; } auto line(LineNum lineNum) { return numWords(lineNum) .iota .map!(wordNum => word(lineNum, wordNum)) .joiner(" ".byCodeUnit); } auto word(LineNum lineNum, WordNum wordNum) { return numCharacters(lineNum, wordNum) .iota .map!(charNum => wordChar(lineNum, wordNum, charNum)); } D2 - Alphabetizing private:
  • 103. D2 - Alphabetizing private: ReturnType!alphabeticIndex index; auto alphabeticIndex() { auto indexOffsets = new LineNum[numLines]; makeIndex!((a, b) => icmp(a, b) < 0) (numLines.iota.map!(a => line(a).array), indexOffsets); return indexOffsets; }
  • 104. D2 - Alphabetizing import two.circularshifter; public: /// (original name: ALPH) void setup() { index = alphabeticIndex; } /// (original name: ITH) LineNum ithLine(LineNum lineNum) { assert(lineNum < index.length); return index[lineNum]; } // The original functions ALPHAC, EQW, ALPHW, EQL a LineNum alphac(LineNum lineNum) {
  • 105. D2 - Output • Module 5: Output. This module will give the desired printing of the set of lines or circular shifts.
  • 106. D2 - Output .line) .each!writeln; } // -- private: auto line(LineNum lineNum) { return numWords(lineNum) .iota .map!(wordNum => word(lineNum, wordNum)) .joiner(" "); } auto word(LineNum lineNum, WordNum wordNum) { return numCharacters(lineNum, wordNum) .iota .map!(charNum => wordChar(lineNum, wordNum, charNum)) .byDchar; }
  • 107. D2 - Output import std.stdio; import std.utf; import two.circularshifter; import two.alphabetizer; public: void printLines() { numLines .iota .map!(lineNum => lineNum .ithLine .line) .each!writeln; } // --
  • 108. D2 - Result A Portrait of The Artist As a Young Man a Young Man A Portrait of The Artist As and The Sea The Old Man Artist As a Young Man A Portrait of The As a Young Man A Portrait of The Artist Ascent of Man The Descent of Man Man A Portrait of The Artist As a Young Man and The Sea The Old Man Descent of Man The Ascent of of Man Descent of Man The Ascent of The Artist As a Young Man A Portrait Old Man and The Sea The Portrait of The Artist As a Young Man A Sea The Old Man and The The Artist As a Young Man A Portrait of The Ascent of Man The Old Man and The Sea The Sea The Old Man and Young Man A Portrait of The Artist As a
  • 109. Comparing Decompositions • The runtime representation of both decompositions might be the same • D2 might have a performance impact • Requires good optimizing compiler
  • 110. Comparing Decompositions • Amenability to change • Comprehensibility • Testability • Parallel Development
  • 111. Changeability • Input format • Store all data in memory • Pack the characters • Create an index of the shifts vs
 store the actual data • When to alphabetize
  • 112. Changeability Module D1 D2 Input ✔ ✔ Line Storage — Circular Shifter Alphabetizer Output • Input format • Store all data in memory • Pack the characters • Create an index of the shifts vs
 store the actual data • When to alphabetize
  • 113. Changeability Module D1 D2 Input ✔ Line Storage — ✔ Circular Shifter ✔ Alphabetizer ✔ Output ✔ • Input format • Store all data in memory • Pack the characters • Create an index of the shifts vs
 store the actual data • When to alphabetize
  • 114. Changeability Module D1 D2 Input ✔ Line Storage — ✔ Circular Shifter ✔ Alphabetizer ✔ Output ✔ • Input format • Store all data in memory • Pack the characters • Create an index of the shifts vs
 store the actual data • When to alphabetize
  • 115. Changeability Module D1 D2 Input Line Storage — Circular Shifter ✔ ✔ Alphabetizer ✔ Output ✔ • Input format • Store all data in memory • Pack the characters • Create an index of the shifts vs
 store the actual data • When to alphabetize
  • 116. Changeability Module D1 D2 Input Line Storage — Circular Shifter Alphabetizer ✔ ✔ Output ✔ • Input format • Store all data in memory • Pack the characters • Create an index of the shifts vs
 store the actual data • When to alphabetize
  • 118. Comprehensibility • Example: understanding the output module • Decomposition 1 module one.output; import std.algorithm; import std.range; import std.stdio; import one.input; import one.circularshifter; void printLines() { shiftIndex.map!(entry => entry.line).each!writeln; } private: auto line(ShiftIndexEntry entry) { auto a = entry.firstChar; auto b = entry.lineNum+1 >= lineIndex.length ? data.length : lineIndex[entry.lineNum+1];
  • 119. Comprehensibility • Example: understanding the output module import one.circularshifter; void printLines() { shiftIndex.map!(entry => entry.line).each!writeln; } private: auto line(ShiftIndexEntry entry) { auto a = entry.firstChar; auto b = entry.lineNum+1 >= lineIndex.length ? data.length : lineIndex[entry.lineNum+1]; auto c = lineIndex[entry.lineNum]; auto d = (entry.firstChar-1).max(0).max(c); auto x = data[a .. b]; auto y = data[c .. d]; return joiner(only(x, y).filter!(a => !a.empty), " "); }
  • 120. Comprehensibility • Example: understanding the output module • Decomposition 2 import std.algorithm; import std.range; import std.stdio; import std.utf; import two.circularshifter; import two.alphabetizer; public: void printLines() { numLines .iota .map!(lineNum => lineNum .ithLine .line) .each!writeln; }
  • 121. Comprehensibility • Example: understanding the output module • Decomposition 2 { numLines .iota .map!(lineNum => lineNum .ithLine .line) .each!writeln; } // -- private: auto line(LineNum lineNum) { return numWords(lineNum) .iota .map!(wordNum => word(lineNum, wordNum)) .joiner(" "); } auto word(LineNum lineNum, WordNum wordNum) {
  • 122. Comprehensibility • Example: understanding the output module • Decomposition 2 // -- private: auto line(LineNum lineNum) { return numWords(lineNum) .iota .map!(wordNum => word(lineNum, wordNum)) .joiner(" "); } auto word(LineNum lineNum, WordNum wordNum) { return numCharacters(lineNum, wordNum) .iota .map!(charNum => wordChar(lineNum, wordNum, charNum)) .byDchar; }
  • 123. Testability • Parnas disputes the idea that information hiding is an empirical result • It’s a mathematical theorem: 1.You have two modules: A, B 2.You can prove A correct knowing only the interface of B 3.You change B without changing the interface 4.Then A doesn’t have to change
  • 124. Testability Module D1 D2 Input ✔ Line Storage — ✔ Circular Shifter ✔ Alphabetizer ✔ Output ✔
  • 125. Parallel Development • Decomposition 1
 • Decomposition 2 M1
 System design
 
 M2
 …
 M1
 System design 
 M2
 …

  • 126. Evaluation • Information hiding: Yay or Nay? • Fred Brook’s The Mythical Man Month
  • 127. Evaluation — MMM "Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won’t usually need your flowcharts; they’ll be obvious.”
  • 128. Evaluation — MMM "Parnas (…) has proposed a still more radical solution. His thesis is that the programmer is most effective if shielded from, rather than exposed to the details of construction of system parts other than his own. This presupposes that all interfaces are completely and precisely defined. While that is definitely sound design, dependence upon its perfect accomplishment is a recipe for disaster.”
  • 129. Evaluation — MMM "Parnas Was Right, and I Was Wrong about Information Hiding
 
 (…) I am now convinced that information hiding, today often embodied in object-oriented programming, is the only way of raising the level of software design.
 (…) [The traditional] technique ensures that programmers can know the detailed semantics of the interfaces they work to by knowing what is on the other side. Hiding those semantics leads to system bugs. On the other hand, Parnas's technique is robust under change and is more appropriate in a design-for-change philosophy. (…)
  • 130. Evaluation • Information hiding: Yay or Nay? • Fred Brooks 👍 • Name • Is decomposition 2 sufficiently good?
  • 131. Master, teach me the true ways of information hiding
  • 132. Accidental Complexity • We already removed some accidental complexity: • Byte-oriented vs word-oriented • Unicode vs weird character comparison functions • Exceptions & assertions vs archaic error routines • Proper naming & namespacing • numWords vs WORDS • CHAR, CSCHAR vs module.wordChar
  • 133. Accidental Complexity • Yet, D2 still has a lot of issues: • Global state • Lack of constructors / initializers • Each function must check if we are in the uninitialized state or in the steady state. • Sequence Interfaces • Memory allocation and data flow • setWordChar(lineNum, wordNum, charNum, c);
  • 134. Sequence Interface • Input: a sequence of lines • Line: a sequence of words • Word: a sequence of characters
  • 135. Sequence Interface f o o ␣ b a r b a z • Input: a sequence of lines • Line: a sequence of words • Word: a sequence of characters
  • 136. Sequence Interface • Input: a sequence of lines • Line: a sequence of words • Word: a sequence of characters f o o b a z b a r line line word word word
  • 137. Sequence Interface • Functions • CHAR • SETCHAR • WORDS • LINES • DELWRD • DELLINE • CHARS f o o ␣ b a r b a z
  • 138. Sequence Interface • Functions • CHAR • SETCHAR • WORDS • LINES • DELWRD • DELLINE • CHARS f o o ␣ b a r b a z E.g. CHAR(r,w,c)
  • 139. Sequence Interface • Functions • DELLINE • LINES • DELWRD • WORDS • CHARS • CHAR • SETCHAR
  • 140. Sequence Interface • Functions • DELLINE • LINES • DELWRD • WORDS • CHARS • CHAR • SETCHAR f o o b a z b a r line line word word word
  • 141. Sequence Interface • Functions • DELLINE • LINES • DELWRD • WORDS • CHARS • CHAR • SETCHAR f o o b a z b a r pointer pointer pointer pointer pointer
  • 142. Sequence Interface • Functions • DELLINE • LINES • DELWRD • WORDS • CHARS • CHAR • SETCHAR
  • 143. Sequence Interface • Functions • DELLINE • LINES • DELWRD • WORDS • CHARS • CHAR • SETCHAR • Input: a sequence of lines • Line: a sequence of words • Word: a sequence of characters 
 sequence<T> ?
  • 144. Push vs Pull Algorithm 1 Target Algorithm 2 Target Algorithm 3 Target Algorithm 1 Algorithm 2 Algorithm 3 Target
  • 146. Decomposition 2 Push vs Pull control Output Alphabetizer Circular shifter Input Line Storage algorithm target
  • 147. Idiomatic Decomposition • Based on hierarchical abstract interfaces • Consistent sequence interface • Pull based • Efficient • D’s ranges and algorithms
  • 152. ID - Input .alphabetized // alphabetizer .each!writeln; // output } // -- private: /// Performs "foo bar n baz" -> [["foo", "bar"], ["baz"]] auto asWordLists(Range)(Range range) { return range .lineSplitter .map!(line => line .splitter!(chr => chr.isWhite) .filter!(word => !word.empty)); } /// Performs [["foo", "bar"], ["baz"]] -> [["foo", "bar"], [" auto withCircularShifts(Range)(Range range) {
  • 153. ID - Circular Shift { return range .lineSplitter .map!(line => line .splitter!(chr => chr.isWhite) .filter!(word => !word.empty)); } /// Performs [["foo", "bar"], ["baz"]] -> /// [["foo", "bar"], ["bar", "foo"], ["baz"]] auto withCircularShifts(Range)(Range range) { return range .map!(line => line.rotations) .joiner; } /// Performs ["foo", "bar"] -> [["foo", "bar"], [" auto rotations(Range)(Range range)
  • 154. ID - Circular Shift /// Performs [["foo", "bar"], ["baz"]] -> /// [["foo", "bar"], ["bar", "foo"], ["baz"]] auto withCircularShifts(Range)(Range range) { return range .map!(line => line.rotations) .joiner; } /// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]] auto rotations(Range)(Range range) { auto len = range.walkLength; return range .repeat(len) .enumerate .map!(item => item.value.cycle.drop(item.index).take(len)); }
  • 155. /// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]] auto rotations(Range)(Range range) { auto len = range.walkLength; return range .repeat(len) .enumerate .map!(item => item.value.cycle.drop(item.index).take(len)); }
  • 156. /// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]] auto rotations(Range)(Range range) { auto len = range.walkLength; return range .repeat(len) .enumerate .map!(item => item.value.cycle.drop(item.index).take(len)); } f o o b a r
  • 157. /// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]] auto rotations(Range)(Range range) { auto len = range.walkLength; return range .repeat(len) .enumerate .map!(item => item.value.cycle.drop(item.index).take(len)); } f o o b a r f o o b a r f o o b a r
  • 158. /// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]] auto rotations(Range)(Range range) { auto len = range.walkLength; return range .repeat(len) .enumerate .map!(item => item.value.cycle.drop(item.index).take(len)); } f o o b a r f o o b a r f o o b a r 0 1
  • 159. /// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]] auto rotations(Range)(Range range) { auto len = range.walkLength; return range .repeat(len) .enumerate .map!(item => item.value.cycle.drop(item.index).take(len)); } f o o b a r f o o b a r f o o b a r 0 1 …f o o b a r f o o b a r f o o b a r …f o o b a r f o o b a r f o o b a r
  • 160. /// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]] auto rotations(Range)(Range range) { auto len = range.walkLength; return range .repeat(len) .enumerate .map!(item => item.value.cycle.drop(item.index).take(len)); } f o o b a r f o o b a r f o o b a r 0 1 …f o o b a r f o o b a r f o o b a r …f o o b a r f o o b a r f o o b a r
  • 161. /// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]] auto rotations(Range)(Range range) { auto len = range.walkLength; return range .repeat(len) .enumerate .map!(item => item.value.cycle.drop(item.index).take(len)); } f o o b a r f o o b a r f o o b a r 0 1 …f o o b a r f o o b a r f o o b a r …b a r f o o b a r f o o b a r f o o b a r b a r f o o
  • 162. ID - Alphabetizing /// Performs [["foo", "bar"], ["baz"]] -> ["baz", "foo bar"] auto alphabetized(Range)(Range range) { return range .map!(line => line.joiner(" ")) .array .sort!((a, b) => icmp(a, b) < 0); }
  • 163. ID - Output /// Performs [["foo", "bar"], ["baz"]] -> ["f auto alphabetized(Range)(Range range) { return range .map!(line => line.joiner(" ")) .array .sort!((a, b) => a.icmp(b) < 0); } void print(Range)(Range range) { range.each!writeln; }
  • 164. ID - Control import std.range; import std.stdio; import std.string; import std.uni; public: void run(string inputFile) { // Original module: readText(inputFile) // input .asWordLists // input .withCircularShifts // circular shifter .alphabetized // alphabetizer .each!writeln; // output } // -- private:
  • 165. void run(string inputFile) { // Original module: readText(inputFile) // input .asWordLists // input .withCircularShifts // circular shifter .alphabetized // alphabetizer .each!writeln; // output } /// Performs "foo bar n baz" -> [["foo", "bar"], ["baz"]] auto asWordLists(Range)(Range range) { return range .lineSplitter .map!(line => line .splitter!isWhite .filter!(word => !word.empty)); } /// Performs [["foo", "bar"], ["baz"]] -> [["foo", "bar"], ["bar", "foo"], ["baz"]] auto withCircularShifts(Range)(Range range) { return range .map!(line => line.rotations) .joiner; } /// Performs ["foo", "bar"] -> [["foo", "bar"], ["bar", "foo"]] auto rotations(Range)(Range range) { auto len = range.walkLength; return range .repeat(len) .enumerate .map!(item => item.value.cycle.drop(item.index).take(len)); } /// Performs [["foo", "bar"], ["baz"]] -> ["baz", "foo bar"] auto alphabetized(Range)(Range range) { return range .map!(line => line.joiner(" ")) .array .sort!((a, b) => icmp(a, b) < 0); }
  • 166. ID - Result A Portrait of The Artist As a Young Man a Young Man A Portrait of The Artist As and The Sea The Old Man Artist As a Young Man A Portrait of The As a Young Man A Portrait of The Artist Ascent of Man The Descent of Man Man A Portrait of The Artist As a Young Man and The Sea The Old Man Descent of Man The Ascent of of Man Descent of Man The Ascent of The Artist As a Young Man A Portrait Old Man and The Sea The Portrait of The Artist As a Young Man A Sea The Old Man and The The Artist As a Young Man A Portrait of The Ascent of Man The Old Man and The Sea The Sea The Old Man and Young Man A Portrait of The Artist As a
  • 167. Conclusion • The idiomatic D decomposition naturally reflects the problem statement • The decomposition that was begging to come out • What Parnas would have done?
  • 168. Conclusion • So, what does Parnas72 mean for D? • D has strong modelling power. What was otherwise complex become, simple, straightforward, even obvious.