ã¯ããã«ïŒ
ç§ã¯ Pythonã§6幎以äž
å·çããŠãããèªåããã®èšèªã®å°é家ãšåŒã¶ããšãã§ããŸãã æè¿ãç§ã¯åœŒã«ã€ããŠã®
æ¬ããæžããã ãã ããéå»8ãæéDã«åãæ¿ãã4ãæéãæšæºã®Phobosã©ã€ãã©ãªã®æ¡åŒµã«é¢ãããã®èšèªã®éçºã«ç©æ¥µçã«é¢äžããŸããã ãŸããè°è«ãããstd.ndsliceã¢ãžã¥ãŒã«ã®ã³ãŒãã¬ãã¥ãŒã«ãåå ããŸããã
Numpyã®ãããªstd.ndsliceã¯ãå€æ¬¡å
é
åã§åäœããããã«èšèšãããŠããŸãã ãã ããNumpyãšã¯ç°ãªããndsliceã¯éåžžã®ã©ã€ãã©ãªã®ããããå Žæã§äœ¿çšãããç¯å²ïŒç¯å²ïŒã«åºã¥ããŠããããããªãŒããŒããããéåžžã«äœããªã£ãŠããŸãã ç¯å²ã䜿çšãããšãäžå¿
èŠãªã³ããŒæé ãåé¿ã§ããé
延èšç®ãçŸããæŽçã§ããŸãã
ãã®èšäºã§ã¯ãstump.ndsliceãNumpyãšæ¯èŒããŠã©ã®ãããªå©ç¹ããããã«ã€ããŠèª¬æããŸãã
ãªãPythonããã°ã©ããŒã¯Dã«ç®ãåããã®ã§ããïŒ
Dã䜿çšãããšãPythonãšã»ãŒåãã·ã³ãã«ã§æå¿«ãªã³ãŒããèšè¿°ã§ããŸããããã®ã³ãŒãã¯Pythonã³ãŒããããã¯ããã«é«éã«åäœããŸãã
次ã®äŸã§ã¯ã
iota
é¢æ°ïŒPythonã®ã¢ããã°ã¯
xrange
ãšåŒã°ã
xrange
ïŒã䜿çšããŠ0ãã999ãŸã§ã®æ°å€ã®ç¯å²ãäœæãã5x5x40ã®æ¬¡å
ã®é
åãè¿ããŸãã
import std.range : iota; import std.experimental.ndslice; void main() { auto slice = sliced(iota(1000), 5, 5, 40); }
Dã¯éçã«åä»ããããèšèªã§ãããåãæ瀺çã«æå®ãããšã³ãŒãã®å¯èªæ§ãåäžãããããPythonããã°ã©ããŒã¯
auto
ããŒã¯ãŒãã䜿çšããŠèªååä»ãã䜿çšããæ¹ãç°¡åã§ãã
sliced
é¢æ°ã¯ãå€æ¬¡å
ã¹ã©ã€ã¹ãè¿ããŸãã å
¥åã§ã¯ãåçŽãªé
åãš
ranges
äž¡æ¹ãåãå
¥ããããšãã§ã
ranges
ã ãã®çµæã0ã999ã®æ°åã§æ§æããã5x5x40ã®ãã¥ãŒããåŸãããŸããã
Rangesãšã¯äœããšããèšèã ããããç¯å²ãšããŠãã·ã¢èªã«ç¿»èš³ããæ¹ãããæ£ç¢ºã§ãã ç¯å²ã䜿çšãããšãã¯ã©ã¹ãŸãã¯æ§é ã«ããããããããããã¿ã€ãã®ããŒã¿ãåæããããã®ã«ãŒã«ãèšè¿°ããããšãã§ããŸãã é¢æ°ãå®è£
ããã ãã§ååã§ã
popFront
ã¯ãç¯å²ã®æåã®èŠçŽ
popFront
ãè¿ããŸã
popFront
ã¯ã次ã®èŠçŽ ã«ç§»åãã空ã§ããæ€çŽ¢å¯Ÿè±¡ã®ã·ãŒã±ã³ã¹ã空ã§ããããšã瀺ãããŒã«å€
empty
è¿ããŸãã
Ranges
䜿çšãããšãå¿
èŠã«å¿ããŠããŒã¿ã«ã¢ã¯ã»ã¹ããŠãé
延å埩ãå®è¡ã§ããŸãã Rangesã®æŠå¿µã«ã€ããŠã¯ã
ããã§è©³ãã説æã
ãŸã ã
空ã®ã¡ã¢ãªå²ãåœãŠããªãããšã«æ³šæããŠãã ããïŒ ããã¯ã
iota
ã§ãé
延ç¯å²ãçæã§ãããããé
延ã¢ãŒãã§
sliced
ãããšã
iota
ããããŒã¿ãååŸããŠãå°çæã«åŠçããããã§ãã
ã芧ã®ãšãããstd.ndsliceã®åäœã¯Numpyãšã¯å°ãç°ãªããŸãã Numpyã¯ããŒã¿çšã«ç¬èªã®ã¿ã€ããäœæããstd.ndsliceã¯æ¢åã®ããŒã¿ãæäœããæ¹æ³ãæäŸããŸãã ããã«ãããç¡é§ãªã¡ã¢ãªå²ãåœãŠã«ãªãœãŒã¹ã浪費ããããšãªããããã°ã©ã å
šäœã§åãããŒã¿ã䜿çšã§ããŸãïŒ ããããœãªã¥ãŒã·ã§ã³ã®ããã©ãŒãã³ã¹ã«éåžžã«æ·±å»ãªåœ±é¿ãäžããããšã¯å®¹æã«æšæž¬ã§ããŸãã
次ã®äŸãèŠãŠã¿ãŸãããã ãã®äžã§ã
stdin
ããããŒã¿ãåãåããäžæã®è¡ã®ã¿ããã£ã«ã¿ãŒåŠçãã䞊ã¹æ¿ããŠãã
stdout
æ»ããŸãã
import std.stdio; import std.array; import std.algorithm; void main() { stdin // get stdin as a range .byLine(KeepTerminator.yes) .uniq // stdin is immutable, so we need a copy .map!(a => a.idup) .array .sort // stdout.lockingTextWriter() is an output range, meaning values can be // inserted into to it, which in this case will be sent to stdout .copy(stdout.lockingTextWriter()); }
é
延ç¯å²ã®çæããã詳现ã«åŠçãããå Žåã¯ã
ãã®èšäºãèªã
ããšããå§ã
ããŸãã
slice
ã¯3ã€ã®æ¬¡å
ããããããããã¯ç¯å²ã®ç¯å²ãè¿ãç¯å²ã§ãã ããã¯ã次ã®äŸã§ã¯ã£ãããšããããŸãã
import std.range : iota; import std.stdio : writeln; import std.experimental.ndslice; void main() { auto slice = sliced(iota(1000), 5, 5, 40); foreach (item; slice) { writeln(item); } }
圌ã®ä»äºã®çµæã¯æ¬¡ã®ããã«ãªããŸãïŒçç¥èšå·ïŒã
[[0, 1, ... 38, 39], [40, 41, ... 78, 79], [80, 81, ... 118, 119], [120, 121, ... 158, 159], [160, 161, ... 198, 199]] ... [[800, 801, ... 838, 839], [840, 841, ... 878, 879], [880, 881, ... 918, 919], [920, 921, ... 958, 959], [960, 961, ... 998, 999]]
foreach
ã¯ãPythonã®
for
foreach
ãšã»ãŒåãããã«æ©èœ
for
foreach
ã ãã ããDã§ã¯ãCã¹ã¿ã€ã«ãšPythonã®ã«ãŒãã®äž¡æ¹ã§äœ¿çšã§ããŸããã
enumerate
ã
xrange
æéã¯
xrange
ã
UFCSïŒUniform Function Call SyntaxïŒã䜿çšãããšã次ã®æ¹æ³ã§ã³ãŒããæžãæããããšãã§ããŸãã
import std.range : iota; import std.experimental.ndslice; void main() { auto slice = 1000.iota.sliced(5, 5, 40); }
UFCSã䜿çš
ãããšãã¡ãœããåŒã³åºãããã§ãŒã³ã§èšé²ãã次ã®ããã«èšè¿°ã§ã
ãŸã ã
a.func(b)
代ããã«ïŒ
func(a, b)
ããããã±ãŒãžãããŒãžã£ãŒã䜿çšããŠç©ºã®ãããžã§ã¯ããçæããŸãããã
dub init
ã³ãã³ãããã³
\source\app.d
次ã®ããã«èšè¿°ããŸãã
import std.experimental.ndslice; void main() { }
çŸåš
std.experimental.ndslice;
std.experimental
ã»ã¯ã·ã§ã³ã«ãããŸãã ããã¯ããããçã§ããããšãæå³ããŸããã ããã¯ã圌ã䞻匵ããå¿
èŠãããããšãæå³ããŸãã
次ã®ã³ãã³ãã§ãããžã§ã¯ããçµã¿ç«ãŠãŸãã
dub
D ndsliceã¢ãžã¥ãŒã«ã¯Numpyã«éåžžã«äŒŒãŠããŸãïŒ
a = numpy.arange(1000).reshape((5, 5, 40)) b = a[2:-1, 1, 10:20]
åçïŒ
auto a = 1000.iota.sliced(5, 5, 40); auto b = a[2 .. $, 1, 10 .. 20];
次ã«ã2次å
é
åãäœæããŠãååã®äžå€®ãååŸããŸãã
PythonïŒ
import numpy data = numpy.arange(100000).reshape((100, 1000)) means = numpy.mean(data, axis=0)
DïŒ
import std.range; import std.algorithm.iteration; import std.experimental.ndslice; import std.array : array; void main() { auto means = 100_000.iota .sliced(100, 1000) .transposed .map!(r => sum(r) / r.length) .array; }
ãã®ã³ãŒããé
延ã¢ãŒãã§åäœããªãããã«ããã«ã¯ã
array
ã¡ãœãããåŒã³åºãå¿
èŠããããŸããã ãã ããå®éã®ã¢ããªã±ãŒã·ã§ã³ã§ã¯ãçµæã¯ããã°ã©ã ã®å¥ã®éšåã§äœ¿çšãããŠããéã¯èšç®ãããŸããã
çŸåšãPhobosã«ã¯çµã¿èŸŒã¿ã®
çµ±èšã¢ãžã¥ãŒã«ã¯ãããŸããã ãããã£ãŠããã®äŸã§ã¯åçŽãªã©ã ãã䜿çšããŠå¹³åå€ãèŠã€ããŸãã
map!
æ©èœ
map!
æåŸã«æå笊ãä»ããŠããŸãã ããã¯ãããããã³ãã¬ãŒãé¢æ°ã§ããããšãæå³ããŸãã æ¬äœã§æå®ãããåŒã«åºã¥ããŠãã³ã³ãã€ã«æ®µéã§ã³ãŒããçæã§ããŸãã
Dã§ãã³ãã¬ãŒãèªäœãã©ã®ããã«æ©èœãããã«ã€ããŠã®è¯ãèšäºããããŸã
ãDã®ã³ãŒãã¯Pythonãããå°ãåé·ã§ããããšãããããŸãããã
map!
ãããã§ã
map!
ã³ãŒãã¯ç¯å²ã§ãããã¹ãŠã®å
¥åã§æ©èœããŸãã Pythonã³ãŒãã¯Numpyã®ç¹å¥ãªé
åã§ã®ã¿æ©èœããŸãã
ããã§ããã®ãã¹ãã®åŸãPythonãæã
Dã倱ããHacker Newsã§å€ãã®è°è«ãè¡ã£ãåŸãééããç¯ããæ¯èŒãå®å
šã«æ£ãããªãããšã«æ°ä»ãããšèšããªããã°ãªããŸããã
iota
ã¯ã
sliced
é¢æ°
sliced
ååŸããããŒã¿ãåçã«äœæããŸãã ãããã£ãŠãæåŸã®åé
眮ã®ç¬éãŸã§ã¡ã¢ãªã«è§ŠããŸããã Dã¯ãããŒã¿åã
long
é
åãè¿ããŸãããNumpyã¯
double
ããè¿ããŸãã ãã®çµæãã³ãŒããæžãçŽããŠãé
åã®å€ã10000ã§ã¯ãªã1000 000ã«ããŸããã次ã«äœãèµ·ãã£ããã瀺ããŸãã
import std.range : iota; import std.array : array; import std.algorithm; import std.datetime; import std.conv : to; import std.stdio; import std.experimental.ndslice; enum test_count = 10_000; double[] means; int[] data; void f0() { means = data .sliced(100, 10000) .transposed .map!(r => sum(r, 0L) / cast(double) r.length) .array; } void main() { data = 1_000_000.iota.array; auto r = benchmark!(f0)(test_count); auto f0Result = to!Duration(r[0] / test_count); f0Result.writeln; }
2.9 GHz Intel Core Broadwell i5ãæèŒãã2015 MacBook Proã§å®æœãããã¹ãã Pythonã§ã¯ãé床ã枬å®ããããã«D
std.datetime.benchmark
%timeit
é¢æ°ã䜿çšããŸããã LDC v0.17ã§æ¬¡ã®ããŒã䜿çšããŠãã¹ãŠãã³ã³ãã€ã«ããŸããïŒ
ldmd2 -release -inline -boundscheck=off -O
ãŸãã¯ãããã䜿çšããå Žåããªãã·ã§ã³
dub --build=release-nobounds --compiler=ldmd2
ã¯ãããã®ããŒã«é¡äŒŒããŠããŸãã
æåã®ãã¹ãã®çµæã¯æ¬¡ã®ãšããã§ãã
Python: 145 µs LDC: 5 µs D is 29x faster
ä¿®æ£ããŒãžã§ã³ã®ãã¹ãã¯æ¬¡ã®ãšããã§ãã
Python: 1.43 msec LDC: 628 ÎŒs D is 2.27x faster
Numpyã¯Cã§æžãããŠãããDã§ã¯èª°ããã¬ããŒãžã³ã¬ã¯ã¿ãŒãscãã®ã§ãããã¯æªãéãã§ã¯ãªãããšã«åæããŸãã
Dã¯ã©ã®ããã«Numpyã®åé¡ãåé¿ããŸãã
ã¯ããNumpyã¯é«éã§ãããåçŽãªPythoné
åãšæ¯èŒããå Žåã®ã¿é«éã§ãã ããããåé¡ã¯ããããã®åçŽãªé
åãšéšåçã«ããäºææ§ããªãããšã§ãã
Numpyã©ã€ãã©ãªã¯ãPythonã®æ®ãã®éšåã®ã©ããã«ãããŸãã 圌女ã¯èªåã®äººçãçããŠããŸãã ç¬èªã®æ©èœã䜿çšãããã®ã¿ã€ãã§åäœããŸãã ããšãã°ãPythonã§NumPyã§äœæãããé
åã䜿çšããå¿
èŠãããå Žåãæ°ããå€æ°ã«ã³ããŒãã
np.asarray
ã䜿çšããå¿
èŠããããŸãã è¿
éãª
githubã®æ€çŽ¢ã«ãããäœåãã®ãããžã§ã¯ãããã®æŸèæã䜿çšããŠããããšãæããã«ãªããŸããã ããŒã¿ã¯ããããã®ç©ºã®ã³ããŒããªããŠããããæ©èœããå¥ã®æ©èœã«ç°¡åã«è»¢éã§ããŸãã
import numpy as np a = [[0.2,0.5,0.3], [0.2,0.5,0.3]] p = np.asarray(a) y = np.asarray([0,1])
圌ãã¯ãPythonæšæºã©ã€ãã©ãªã®äžéšãæžãæããŠNumpyåã䜿çšããããšã§ããã®åé¡ã解決ããããšããŠããŸãã ãã ããããã¯ãŸã å®å
šãªè§£æ±ºçã§ã¯ãªããããäœææã«çŽ æŽããããžã§ãŒã¯ãçºçããŸãã
sum(a)
代ããã«ïŒ
a.sum()
é床ã10åäœäžããŸãã
Dã¯ãåã«èšèšäžãã®ãããªåé¡ãæ±ããŠããŸããã ããã¯ãã³ã³ãã€ã«æžã¿ã®éçã«åæå®ãããèšèªã§ãã ã³ãŒãçæäžã«ããã¹ãŠã®ã¿ã€ãã®å€æ°ãèªèãããŸãã std.ndsliceèªäœã§ã¯ãããšãã°std.algorithmãstd.rangeãªã©ã®ãã°ãããæ©èœãªã©ãPhobosã©ã€ãã©ãªã®ãã¹ãŠã®æ©èœã«å®å
šã«ã¢ã¯ã»ã¹ã§ããŸãã ãããã³ã³ãã€ã«æ®µéã§
Dãã³ãã¬ãŒãã䜿çšããŠã³ãŒããçæã§ããŸãã
以äžã«äŸã瀺ããŸãã
import std.range : iota; import std.algorithm.iteration : sum; import std.experimental.ndslice; void main() { auto slice = 1000.iota.sliced(5, 5, 40); auto result = slice // sum expects an input range of numerical values, so to get one // we call std.experimental.ndslice.byElement to get the unwound // range .byElement .sum; }
ããªãã¯ã
sum
é¢æ°ãåãã䜿çšããã ãã§ãåºæ¬ã©ã€ãã©ãªã®ä»ã®é¢æ°ãšåæ§ã«ãåããæ©èœããŸãã
Pythonèªäœã§ãç¹å®ã®å€ã§åæåãããå®çŸ©æžã¿ã®é·ãã®ãªã¹ããååŸããã«ã¯ã次ã®ããã«èšè¿°ããå¿
èŠããããŸãã
a = [0] * 1000
Numpyã¯å®å
šã«ç°ãªããŸãã
a = numpy.zeros((1000))
ãŸããNumpyã䜿çšããªãå Žåãã¡ã¢ãªãæ¶è²»ããäžèŠãªã³ããŒæäœãããã³ãŒãã4åé
ããªããŸãã ç¯å²ã¯Dã®å©ãã«ãªããŸããããã«ããã空ã®ã³ããŒæäœãããã«ãåãæäœããã°ããå®è¡ã§ããŸãã
auto a = repeat(0, 1000).array;
ãŸããå¿
èŠã«å¿ããŠãããã«ndsliceãåŒã³åºãããšãã§ããŸãã
auto a = repeat(0, 1000).array.sliced(5, 5, 40);
çŸåšã®Numpyã®äž»ãªå©ç¹ã¯ããã®æ®åçã§ãã çŸåšã§ã¯ãéè¡ã·ã¹ãã ããæ©æ¢°åŠç¿ãŸã§éåžžã«åºã䜿çšãããŠããŸãã å€ãã®æ¬ãäŸãèšäºããããŸãã ãã ããDã®æ°åŠçå¯èœæ§ã¯æããã«ããã«æ¡åŒµãããã§ãããã ãã®ãããndsliceã®äœè
ã¯ãçŸåšphosã®BLASïŒBasic Linear Algebra SubprogramsïŒã«åãçµãã§ããããããndsliceããã³æšæºã©ã€ãã©ãªãšå®å
šã«
çµ±åããããšè¿°ã¹ãŠããŸãã
匷åãªæ°åŠçãµãã·ã¹ãã ã«ãããããã°ããŒã¿ãæ±ãå¿
èŠãããå€ãã®åé¡ãéåžžã«å¹ççã«è§£æ±ºã§ããŸãã ããšãã°ãã³ã³ãã¥ãŒã¿ãŒããžã§ã³ã·ã¹ãã ã ãããã®ã·ã¹ãã ã®1ã€ã®ãããã¿ã€ãã¯ãã§ã«éçºäžã§ã
DCVãšåŒã°ããŠããŸãã
Dã®ç»ååŠç
次ã®äŸã¯ãã¡ãã£ã¢ã³ãã£ã«ã¿ãŒãç»åã®ãã€ãºãé€å»ããæ¹æ³ã瀺ããŠããŸãã
movingWindowByChannel
é¢æ°ã¯ãã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠãå¿
èŠãªä»ã®ãã£ã«ã¿ãŒã§ã䜿çšã§ããŸãã
movingWindowByChannel
䜿çš
movingWindowByChannel
ãšãã¹ã©ã€ãã£ã³ã°ãŠã£ã³ããŠã䜿çšããŠç»åå
ã移åã§ã
movingWindowByChannel
ã ãã®ãããªåãŠã£ã³ããŠã¯ãéžæãããŸãŒã³ã«åºã¥ããŠãã¯ã»ã«å€ãèšç®ãããã£ã«ã¿ãŒã«æž¡ãããŸãã
ãã®é¢æ°ã¯ãéšåçã«éè€ããé åãåŠçããŸããã ãã ãããã®å©ããåããŠããããã®å€ãèšç®ã§ããŸãã ãããè¡ãã«ã¯ãå
ã®ç»åã®å¢çç·ãåæ ãããšããžãæã€æ¡å€§ç»åãäœæããŠããåŠçããŸãã
Slice!(3, C*) movingWindowByChannel(alias filter, C) (Slice!(3, C*) image, size_t nr, size_t nc) { // local imports in D work much like Python's local imports, // meaning if your code never runs this function, these will // never be imported because this function wasn't compiled import std.algorithm.iteration: map; import std.array: array; // 0. 3D // The last dimension represents the color channel. auto wnds = image // 1. 2D composed of 1D // Packs the last dimension. .pack!1 // 2. 2D composed of 2D composed of 1D // Splits image into overlapping windows. .windows(nr, nc) // 3. 5D // Unpacks the windows. .unpack // 4. 5D // Brings the color channel dimension to the third position. .transposed!(0, 1, 4) // 5. 3D Composed of 2D // Packs the last two dimensions. .pack!2; return wnds // 6. Range composed of 2D // Gathers all windows in the range. .byElement // 7. Range composed of pixels // 2D to pixel lazy conversion. .map!filter // 8. `C[]` // The only memory allocation in this function. .array // 9. 3D // Returns slice with corresponding shape. .sliced(wnds.shape); }
次ã®é¢æ°ã¯ããªããžã§ã¯ãã®äžå€®å€ãèšç®ããæ¹æ³ã®äŸã§ãã èªã¿ãããããããã«ããã®æ©èœã¯å€§å¹
ã«ç°¡çŽ åãããŠããŸãã
T median(Range, T)(Range r, T[] buf) { import std.algorithm.sorting: sort; size_t n; foreach (e; r) { buf[n++] = e; } buf[0 .. n].sort(); immutable m = n >> 1; return n & 1 ? buf[m] : cast(T)((buf[m - 1] + buf[m]) / 2); }
ããŠãä»ã¡ã€ã³èªäœïŒ
void main(string[] args) { import std.conv: to; import std.getopt: getopt, defaultGetoptPrinter; import std.path: stripExtension; // In D, getopt is part of the standard library uint nr, nc, def = 3; auto helpInformation = args.getopt( "nr", "number of rows in window, default value is " ~ def.to!string, &nr, "nc", "number of columns in window, default value is equal to nr", &nc); if (helpInformation.helpWanted) { defaultGetoptPrinter( "Usage: median-filter [<options...>] [<file_names...>]\noptions:", helpInformation.options); return; } if (!nr) nr = def; if (!nc) nc = nr; auto buf = new ubyte[nr * nc]; foreach (name; args[1 .. $]) { import imageformats; // can be found at code.dlang.org IFImage image = read_image(name); auto ret = image.pixels .sliced(cast(size_t)image.h, cast(size_t)image.w, cast(size_t)image.c) .movingWindowByChannel !(window => median(window.byElement, buf)) (nr, nc); write_image( name.stripExtension ~ "_filtered.png", ret.length!1, ret.length!0, (&ret[0, 0, 0])[0 .. ret.elementsCount]); } }
ãã¹ãŠã®äŸãæ確ã«èŠããªãå Žåã¯ããã°ãããæ¬ã
Programming in D ãã®ç¡æçãèªãããšããå§ãããŸã
ãPSãã®åºçç©ãã翻蚳ãã®ã¹ããŒã¿ã¹ã«ç¿»èš³ããæ¹æ³ãç¥ã£ãŠãããªãããã©ã€ããŒãã§æžããŠãã ããã