Created
September 11, 2013 07:22
-
-
Save astynax/6520298 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"metadata": { | |
"name": "" | |
}, | |
"nbformat": 3, | |
"nbformat_minor": 0, | |
"worksheets": [ | |
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# \u0421\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u044b, greenlets \u0438 \u043f\u0440\u043e\u0447" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### \u0412\u0441\u043f\u043e\u043c\u043d\u0438\u043c \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u044b" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def f():\n", | |
" yield 'a'\n", | |
" yield 'b'\n", | |
" yield 'c'" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 1 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"ff = f()\n", | |
"print ff.next()\n", | |
"print ff.next()\n", | |
"print ff.next()\n", | |
"print ff.next() # <- \u0442\u0443\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"ename": "StopIteration", | |
"evalue": "", | |
"output_type": "pyerr", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mStopIteration\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-2-bb1aa0307e4c>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[0mff\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[1;32mprint\u001b[0m \u001b[0mff\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[1;32mprint\u001b[0m \u001b[0mff\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# <- \u0442\u0443\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", | |
"\u001b[1;31mStopIteration\u001b[0m: " | |
] | |
}, | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"a\n", | |
"b\n", | |
"c\n" | |
] | |
} | |
], | |
"prompt_number": 2 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### \u0421\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def f():\n", | |
" print \"Started!\"\n", | |
" while True:\n", | |
" x = (yield) # \u0441\u043a\u043e\u0431\u043a\u0438 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b!\n", | |
" print '>', x" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 3 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"ff = f()\n", | |
"print ff" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"<generator object f at 0x93dac34>\n" | |
] | |
} | |
], | |
"prompt_number": 4 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c - \u044d\u0442\u043e \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c. \u0421\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u0441\u044f \u0434\u043e \u043f\u0435\u0440\u0432\u043e\u0433\u043e ```yield``` \u0438 \u043f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043d\u0430 \u043d\u0435\u043c." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"ff.next()" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"Started!\n" | |
] | |
} | |
], | |
"prompt_number": 5 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0442\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f!" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"ff.send(20)" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"> 20\n" | |
] | |
} | |
], | |
"prompt_number": 6 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0421\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430 \u0432\u043e\u0437\u0431\u0443\u0436\u0434\u0435\u0442 StopIteration \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435 \u0438\u0437 \u0435\u0451 \u0442\u0435\u043b\u0430" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def g():\n", | |
" x = (yield)\n", | |
" y = (yield)\n", | |
" print x + y" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 7 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"gg = g()" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 8 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"gg.next()\n", | |
"gg.send(10)\n", | |
"gg.send(20)" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"ename": "StopIteration", | |
"evalue": "", | |
"output_type": "pyerr", | |
"traceback": [ | |
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mStopIteration\u001b[0m Traceback (most recent call last)", | |
"\u001b[1;32m<ipython-input-9-498863336f61>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 1\u001b[0m \u001b[0mgg\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mnext\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[0mgg\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msend\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m10\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 3\u001b[1;33m \u001b[0mgg\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0msend\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m20\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", | |
"\u001b[1;31mStopIteration\u001b[0m: " | |
] | |
}, | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"30\n" | |
] | |
} | |
], | |
"prompt_number": 9 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0418\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432\u043e\u0437\u0431\u0443\u0436\u0434\u0430\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0433\u043e \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0441\u0442\u0435\u043a\u0430 \u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## dataflow-\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\n", | |
"\n", | |
"- \u0438\u0441\u0442\u043e\u043a\u0438\n", | |
"- \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438\n", | |
"- \u0441\u0442\u043e\u043a\u0438" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"**Dataflow** \u043d\u0430 **\u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430\u0445** - *\u043f\u0440\u043e\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043d\u0438\u0435* \u0434\u0430\u043d\u043d\u044b\u0445:\n", | |
"\n", | |
" \u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a -(send)-> \u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430-\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a -(send)-> ... -(send)-> \u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430-\u0441\u0442\u043e\u043a\n", | |
"\n", | |
"**Dataflow** \u043d\u0430 **\u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440\u0430\u0445** - *\u0432\u044b\u0442\u044f\u0433\u0438\u0432\u0430\u043d\u0438\u0435* \u0434\u0430\u043d\u043d\u044b\u0445:\n", | |
"\n", | |
" \u0421\u0442\u043e\u043a <-(next)- \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440-\u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <-(next)- ... <-(next)- \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440-\u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def coroutine(fn):\n", | |
" \"\"\"\u0414\u0435\u043a\u043e\u0440\u0430\u0442\u043e\u0440, \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u044e\u0449\u0438\u0439 \u0437\u0430\u043f\u0443\u0441\u043a \u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u044b \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435\"\"\"\n", | |
" def wrapper(*args, **kwargs):\n", | |
" obj = fn(*args, **kwargs)\n", | |
" obj.next()\n", | |
" return obj\n", | |
" return wrapper\n", | |
"\n", | |
"\n", | |
"def source(iterable, target):\n", | |
" \"\"\"\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a \"\u0441\u043a\u0430\u0440\u043c\u043b\u0438\u0432\u0430\u044e\u0449\u0438\u0439\" \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440\u0430 \u0432 \u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0443-\u043f\u043e\u043b\u0443\u0447\u0430\u0442\u0435\u043b\u044c\"\"\"\n", | |
" for i in iterable:\n", | |
" target.send(i)\n", | |
"\n", | |
"\n", | |
"@coroutine\n", | |
"def filter_(pred, target):\n", | |
" \"\"\"\u0421\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430-\u0444\u0438\u043b\u044c\u0442\u0440\"\"\"\n", | |
" while True:\n", | |
" x = (yield)\n", | |
" if pred(x):\n", | |
" target.send(x)\n", | |
"\n", | |
"@coroutine\n", | |
"def map_(fn, target):\n", | |
" \"\"\"\u0421\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430 - \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u0435\u043b\u044c\"\"\"\n", | |
" while True:\n", | |
" target.send(fn((yield)))\n", | |
"\n", | |
"@coroutine\n", | |
"def broadcast(*targets):\n", | |
" \"\"\"\u0421\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430 - \u0440\u0430\u0437\u0432\u0435\u0442\u0432\u0438\u0442\u0435\u043b\u044c\"\"\"\n", | |
" while True:\n", | |
" x = (yield)\n", | |
" for t in targets:\n", | |
" t.send(x)\n", | |
"\n", | |
"@coroutine\n", | |
"def printer(fmt=\"%s\"):\n", | |
" \"\"\"\u0421\u0442\u043e\u043a, \u0432\u044b\u0432\u043e\u0434\u044f\u0449\u0438\u0439 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434\"\"\"\n", | |
" while True:\n", | |
" print fmt % ((yield))\n", | |
"\n", | |
"\n", | |
"@coroutine\n", | |
"def reducer(fn, initial, target):\n", | |
" \"\"\"\u0421\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u044f\u0449\u0430\u044f \u0441\u0432\u0435\u0440\u0442\u043a\u0443 \u0434\u0430\u043d\u043d\u044b\u0445\"\"\"\n", | |
" res = initial\n", | |
" while True:\n", | |
" try:\n", | |
" res = fn(res, (yield))\n", | |
" except GeneratorExit:\n", | |
" target.send(res)\n", | |
" raise StopIteration()\n", | |
"\n", | |
"@coroutine\n", | |
"def caller(fn):\n", | |
" \"\"\"\u0421\u0442\u043e\u043a, \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0438\u0439 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e\"\"\"\n", | |
" while True:\n", | |
" fn((yield))" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 10 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"l = []\n", | |
"print source(\n", | |
" [1,3,5,7],\n", | |
" broadcast(\n", | |
" caller(l.append),\n", | |
" filter_(\n", | |
" lambda x: x > 2,\n", | |
" map_(\n", | |
" lambda x: x * 10,\n", | |
" printer())))), l" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"30\n", | |
"50\n", | |
"70\n", | |
"None [1, 3, 5, 7]\n" | |
] | |
} | |
], | |
"prompt_number": 11 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u041f\u0438\u0441\u0430\u0442\u044c \u0442\u0430\u043a\u0438\u0435 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u0446\u0435\u043f\u043e\u0447\u043a\u0438 \u0441\u043b\u043e\u0436\u043d\u043e, \u0430 \u0447\u0438\u0442\u0430\u0442\u044c \u0435\u0449\u0451 \u0441\u043b\u043e\u0436\u043d\u0435\u0435. \u0423\u043f\u0440\u043e\u0441\u0442\u0438\u043c \u0436\u0438\u0437\u043d\u044c - \u043d\u0430\u043f\u0438\u0448\u0435\u043c \u043a\u043e\u043d\u0432\u0435\u0435\u0440:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def chain(*steps):\n", | |
" \"\"\"\n", | |
" \u041f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0438\u0434\u0430:\n", | |
" [\n", | |
" (\u0444\u0443\u043d\u043a\u0446\u0438\u044f_\u043f\u043e\u043f\u0440\u043e\u0436\u0434\u0430\u044e\u0449\u0430\u044f_\u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0443, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435, \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b, \u043a\u0440\u043e\u043c\u0435, target),\n", | |
" ...\n", | |
" \u0444\u0443\u043d\u043a\u0446\u0438\u044f_\u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0430\u044f_\u0442\u043e\u043b\u044c\u043a\u043e_target,\n", | |
" ...\n", | |
" ]\n", | |
" \u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0443, \u043f\u0440\u043e\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u044e\u0449\u0443\u044e \u0434\u0430\u043d\u043d\u044b\u0435 \u0447\u0435\u0440\u0435\u0437 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u0438\u0441\u043e\u043a \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432\n", | |
" \"\"\"\n", | |
" steps = steps[::-1]\n", | |
" arg = steps[0]\n", | |
" if callable(arg):\n", | |
" arg = arg()\n", | |
" for s in steps[1:]:\n", | |
" if callable(s):\n", | |
" arg = s(arg)\n", | |
" else:\n", | |
" arg = s[0](*(tuple(s[1:]) + (arg,)))\n", | |
" return arg" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 12 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"source(\n", | |
" xrange(10),\n", | |
" chain(\n", | |
" (filter_, lambda x: x % 2 == 0), # \u0431\u0435\u0440\u0435\u043c \u0447\u0451\u0442\u043d\u044b\u0435\n", | |
" (map_, str), # \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u043c \u043a \u0441\u0442\u0440\u043e\u043a\u0435\n", | |
" (map_, lambda x: x*3), # \u0443\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c\n", | |
" printer() # \u0432\u044b\u0432\u043e\u0434\u0438\u043c \u043d\u0430 \u043f\u0435\u0447\u0430\u0442\u044c\n", | |
" )\n", | |
")" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"000\n", | |
"222\n", | |
"444\n", | |
"666\n", | |
"888\n" | |
] | |
} | |
], | |
"prompt_number": 13 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0415\u0448\u0451 \u043f\u0430\u0440\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 - \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u0435\u043b\u0438:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"@coroutine\n", | |
"def take(n, target):\n", | |
" \"\"\"\u041f\u0440\u043e\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u0442 @n \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0430 \u0432\u0441\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 - \u043e\u0442\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442\"\"\"\n", | |
" cnt = 0\n", | |
" while True:\n", | |
" i = (yield)\n", | |
" if cnt < n:\n", | |
" target.send(i)\n", | |
" cnt += 1\n", | |
"\n", | |
"@coroutine\n", | |
"def drop(n, target):\n", | |
" \"\"\"\u041e\u0442\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 @n \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0430 \u0432\u0441\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 - \u043f\u0440\u043e\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u0435\u0442\"\"\"\n", | |
" cnt = n\n", | |
" while True:\n", | |
" i = (yield)\n", | |
" if cnt == 0:\n", | |
" target.send(i)\n", | |
" else:\n", | |
" cnt -= 1" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 14 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"l = []\n", | |
"source(\n", | |
" xrange(100),\n", | |
" chain(\n", | |
" (drop, 51),\n", | |
" (take, 7),\n", | |
" caller(l.append)))\n", | |
"print l" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"[51, 52, 53, 54, 55, 56, 57]\n" | |
] | |
} | |
], | |
"prompt_number": 15 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u041f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0441\u0432\u0435\u0440\u0442\u043a\u0443:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"source(\n", | |
" [1, 2, 3],\n", | |
" reducer(\n", | |
" lambda x, y: x + y,\n", | |
" 0, # \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\n", | |
" printer()))" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"6\n" | |
] | |
} | |
], | |
"prompt_number": 16 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0421\u0432\u0435\u0440\u0442\u043a\u0430, \u043e\u0436\u0438\u0434\u0430\u044e\u0449\u0430\u044f \u043f\u0435\u0440\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"@coroutine\n", | |
"def reducer1(fn, target):\n", | |
" try:\n", | |
" res = (yield)\n", | |
" while True:\n", | |
" res = fn(res, (yield))\n", | |
" except GeneratorExit:\n", | |
" try:\n", | |
" target.send(res)\n", | |
" except UnboundLocalError:\n", | |
" pass\n", | |
" raise StopIteration()" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 17 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"source(\n", | |
" [1, 2, 3],\n", | |
" reducer1(\n", | |
" lambda x, y: x + y,\n", | |
" printer()))" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"6\n" | |
] | |
} | |
], | |
"prompt_number": 18 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u0430\u0445\u0430\u0440\u0430 - \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c **\u043a\u0430\u043d\u0430\u043b\u044b** \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"class Pipe(object):\n", | |
" def __init__(self, *args):\n", | |
" self._steps = args\n", | |
" self._chain = None\n", | |
"\n", | |
" def __rshift__(self, other):\n", | |
" if isinstance(other, self.__class__):\n", | |
" return self.__class__(*(self._steps + other._steps))\n", | |
" else:\n", | |
" return self.__class__(*(self._steps + (other,)))\n", | |
" \n", | |
" def send(self, data):\n", | |
" if not self._chain:\n", | |
" assert self._steps\n", | |
" self._chain = chain(*self._steps)\n", | |
" self._chain.send(data)\n", | |
" \n", | |
" def close(self):\n", | |
" assert self._chain\n", | |
" self._chain.close()\n", | |
"\n", | |
" def __call__(self, iterable):\n", | |
" assert self._steps\n", | |
" return source(iterable, chain(*self._steps))" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 19 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"(\n", | |
" Pipe() >> (reducer1, lambda x, y: x + y) >> printer\n", | |
")(\n", | |
" [1,2,3]\n", | |
")" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"6\n" | |
] | |
} | |
], | |
"prompt_number": 20 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u041f\u0440\u0438\u0447\u0435\u043c, \u043a\u0430\u043d\u0430\u043b\u044b \u043c\u043e\u0436\u043d\u043e **\u043a\u043e\u043c\u0431\u0438\u043d\u0438\u0440\u043e\u0432\u0430\u0442\u044c**:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"repr_printer = Pipe() >> (map_, repr) >> printer\n", | |
"none_filter = Pipe() >> (filter_, lambda x: x is not None)\n", | |
"\n", | |
"(none_filter >> repr_printer)([1,\"asd\", None, True])" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"1\n", | |
"'asd'\n", | |
"True\n" | |
] | |
} | |
], | |
"prompt_number": 21 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0411\u043e\u043b\u0435\u0435 \u043a\u043e\u043c\u043f\u043b\u0435\u043a\u0441\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 - \u0441\u0431\u043e\u0440 \u0441\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0438 \u043f\u043e \u043d\u0430\u0431\u043e\u0440\u0443 \u0447\u0438\u0441\u0435\u043b:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"import operator as op\n", | |
"\n", | |
"def stat(iterable):\n", | |
" res = {}\n", | |
"\n", | |
" def setter(d, k):\n", | |
" return lambda v: d.__setitem__(k, v)\n", | |
" (\n", | |
" Pipe() >> broadcast(\n", | |
" Pipe() >> (reducer1, max) >> caller(setter(res, 'max')),\n", | |
" Pipe() >> (reducer1, min) >> caller(setter(res, 'min')),\n", | |
" Pipe() >> (reducer1, op.add) >> caller(setter(res, 'sum'))\n", | |
" )\n", | |
" )(\n", | |
" iterable\n", | |
" )\n", | |
" return res\n", | |
"\n", | |
"print stat([1,2,5,2,3,-1,56, -15])" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"{'max': 56, 'sum': 53, 'min': -15}\n" | |
] | |
} | |
], | |
"prompt_number": 22 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## \u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u0430\u044f \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u044c" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 \u0437\u0430\u0434\u0430\u0447:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"class Scheduler(object):\n", | |
"\n", | |
" _tick = 0\n", | |
" _stop_flag = None\n", | |
" \n", | |
" def __init__(self):\n", | |
" self._queue = []\n", | |
" \n", | |
" @property\n", | |
" def tick(self):\n", | |
" return self._tick\n", | |
"\n", | |
" def run(self):\n", | |
" self._stop_flag = False\n", | |
" self._tick = 1\n", | |
" # \u0437\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447\n", | |
" for t in self._queue:\n", | |
" t.next()\n", | |
" t.send(self)\n", | |
" # \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0446\u0438\u043a\u043b\n", | |
" while self._queue:\n", | |
" for t in self._queue[:]:\n", | |
" try:\n", | |
" t.send(self._stop_flag)\n", | |
" except StopIteration:\n", | |
" # \u0435\u0441\u043b\u0438 \u0437\u0430\u0434\u0430\u0447\u0430 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0430\u0441\u044c, \u0432\u044b\u043a\u0438\u0434\u044b\u0432\u0430\u0435\u043c \u0435\u0451 \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438\n", | |
" self._queue.remove(t)\n", | |
" self._tick += 1\n", | |
" \n", | |
" def stop(self):\n", | |
" self._stop_flag = True\n", | |
" \n", | |
" def add(self, task):\n", | |
" if not self._stop_flag:\n", | |
" # \u0435\u0441\u043b\u0438 \u0438\u0434\u0451\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\n", | |
" if self._tick:\n", | |
" # \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u0440\u0430\u0437\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f\n", | |
" task.next()\n", | |
" task.send(self)\n", | |
" self._queue.append(task)\n", | |
" return task" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 23 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u041f\u0430\u0440\u0430 \u0437\u0430\u0434\u0430\u0447" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def ticker(msg, interval=1):\n", | |
" sh = (yield) # \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440\n", | |
" while True:\n", | |
" # \u043f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0443\n", | |
" if (yield):\n", | |
" # \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 \u043c\u043e\u0436\u0435\u0442 \u0441\u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0432\u0430\u0442\u044c \"\u0441\u0442\u043e\u043f!\"\n", | |
" print msg, '<- stopped!'\n", | |
" break\n", | |
" if sh.tick % interval == 0:\n", | |
" print msg\n", | |
"\n", | |
"def stop_at(tick):\n", | |
" sh = (yield)\n", | |
" while True:\n", | |
" if (yield):\n", | |
" break\n", | |
" if sh.tick >= tick:\n", | |
" sh.stop() # \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u043e\u043c\u0430\u043d\u0434\u043e\u0432\u0430\u0442\u044c \"\u0441\u0442\u043e\u043f!\" \u0438 \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440\n", | |
"\n", | |
"def respawner():\n", | |
" sh = (yield)\n", | |
" if not (yield):\n", | |
" print \"Respawn! Tick:\", sh.tick\n", | |
" sh.add(respawner()) # \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440\u0430" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 24 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c!" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"sh = Scheduler()\n", | |
"sh.add(stop_at(20))\n", | |
"sh.add(ticker(\"Every 3!\", 3))\n", | |
"sh.add(ticker(\"Every 5!\", 5))\n", | |
"sh.add(respawner())\n", | |
"sh.run()" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"Respawn! Tick: 1\n", | |
"Respawn! Tick: 2\n", | |
"Every 3!\n", | |
"Respawn! Tick: 3\n", | |
"Respawn! Tick: 4\n", | |
"Every 5!\n", | |
"Respawn! Tick: 5\n", | |
"Every 3!\n", | |
"Respawn! Tick: 6\n", | |
"Respawn! Tick: 7\n", | |
"Respawn! Tick: 8\n", | |
"Every 3!\n", | |
"Respawn! Tick: 9\n", | |
"Every 5!\n", | |
"Respawn! Tick: 10\n", | |
"Respawn! Tick: 11\n", | |
"Every 3!\n", | |
"Respawn! Tick: 12\n", | |
"Respawn! Tick: 13\n", | |
"Respawn! Tick: 14\n", | |
"Every 3!\n", | |
"Every 5!\n", | |
"Respawn! Tick: 15\n", | |
"Respawn! Tick: 16\n", | |
"Respawn! Tick: 17\n", | |
"Every 3!\n", | |
"Respawn! Tick: 18\n", | |
"Respawn! Tick: 19\n", | |
"Every 3! <- stopped!\n", | |
"Every 5! <- stopped!\n" | |
] | |
} | |
], | |
"prompt_number": 25 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u041c\u043e\u0436\u043d\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0432 \u0432\u0438\u0434\u0435 \u043a\u043b\u0430\u0441\u0441\u043e\u0432" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"class Counter(object):\n", | |
" def __init__(self, sch):\n", | |
" self._sch = sch\n", | |
" self._cnt = 0\n", | |
"\n", | |
" def step(self):\n", | |
" self._cnt += 1\n", | |
"\n", | |
" def stop(self):\n", | |
" print 'Count:', self._cnt\n", | |
"\n", | |
"class Stopper(object):\n", | |
" def __init__(self, sch, stop_at):\n", | |
" self._sch = sch\n", | |
" self._stop_at = stop_at\n", | |
"\n", | |
" def step(self):\n", | |
" if self._sch.tick >= self._stop_at:\n", | |
" self._sch.stop()\n", | |
"\n", | |
" def stop(self):\n", | |
" pass" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 26 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0422\u0435\u043f\u0435\u0440\u044c, \u0435\u0441\u043b\u0438 \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u043e\u0431\u0451\u0440\u0442\u043a\u043e\u0439..." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def as_task(task_clz, *args, **kwargs):\n", | |
" sch = (yield)\n", | |
" obj = task_clz(sch, *args, **kwargs)\n", | |
" while True:\n", | |
" if (yield):\n", | |
" obj.stop()\n", | |
" break\n", | |
" if obj.step():\n", | |
" break" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 27 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"...\u0442\u043e Scheduler \u0441\u043c\u043e\u0436\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043e\u0431\u0451\u0440\u043d\u0443\u0442\u044b\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u043d\u0430\u0440\u044f\u0434\u0443 \u0441 \"\u043e\u0431\u044b\u0447\u043d\u044b\u043c\u0438\" \u0437\u0430\u0434\u0430\u0447\u0430\u043c\u0438:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"sch = Scheduler()\n", | |
"sch.add(as_task(Counter))\n", | |
"sch.add(as_task(Stopper, stop_at=10))\n", | |
"sch.add(respawner())\n", | |
"sch.run()" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"Respawn! Tick: 1\n", | |
"Respawn! Tick: 2\n", | |
"Respawn! Tick: 3\n", | |
"Respawn! Tick: 4\n", | |
"Respawn! Tick: 5\n", | |
"Respawn! Tick: 6\n", | |
"Respawn! Tick: 7\n", | |
"Respawn! Tick: 8\n", | |
"Respawn! Tick: 9\n", | |
"Count: 10\n" | |
] | |
} | |
], | |
"prompt_number": 28 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0417\u0430\u0434\u0430\u0447\u0438 \u043c\u043e\u0433\u0443\u0442 \u043f\u043b\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0438\u0438!" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"class Swarm(object):\n", | |
" def __init__(self, sch, gen=1):\n", | |
" self._sch = sch\n", | |
" self._gen = gen\n", | |
" print 'I`m from gen %d!' % gen\n", | |
" def step(self):\n", | |
" self._sch.add(as_task(Swarm, self._gen + 1))\n", | |
" self._sch.add(as_task(Swarm, self._gen + 1))\n", | |
" return True # \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c \"\u0443\u043c\u0438\u0440\u0430\u0435\u0442\"\n", | |
" def stop(self):\n", | |
" pass" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 29 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"sch = Scheduler()\n", | |
"sch.add(as_task(Swarm))\n", | |
"sch.add(stop_at(5))\n", | |
"sch.run()" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"I`m from gen 1!\n", | |
"I`m from gen 2!\n", | |
"I`m from gen 2!\n", | |
"I`m from gen 3!\n", | |
"I`m from gen 3!\n", | |
"I`m from gen 3!\n", | |
"I`m from gen 3!\n", | |
"I`m from gen 4!\n", | |
"I`m from gen 4!\n", | |
"I`m from gen 4!\n", | |
"I`m from gen 4!\n", | |
"I`m from gen 4!\n", | |
"I`m from gen 4!\n", | |
"I`m from gen 4!\n", | |
"I`m from gen 4!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n", | |
"I`m from gen 5!\n" | |
] | |
} | |
], | |
"prompt_number": 30 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u043f\u0438\u0448\u0435\u043c\n", | |
"## \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0432\u0438\u043d\u0443\u0442\u044b\u0439 \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"class AdvScheduler(object):\n", | |
" \n", | |
" _tick = 0\n", | |
" _stop_flag = None\n", | |
" \n", | |
" def __init__(self):\n", | |
" self._queue = []\n", | |
" \n", | |
" @property\n", | |
" def tick(self):\n", | |
" return self._tick\n", | |
"\n", | |
" def run(self):\n", | |
" self._stop_flag = False\n", | |
" self._tick = 1\n", | |
" # \u0437\u0430\u043f\u0443\u0441\u043a \u0437\u0430\u0434\u0430\u0447\n", | |
" for t in self._queue:\n", | |
" t.next()\n", | |
" t.send(self)\n", | |
" # \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0446\u0438\u043a\u043b\n", | |
" while self._queue:\n", | |
" for t in self._queue[:]:\n", | |
" try:\n", | |
" t.send(None)\n", | |
" except StopIteration:\n", | |
" self._queue.remove(t)\n", | |
"\n", | |
" if self._stop_flag:\n", | |
" for t in self._queue[:]:\n", | |
" t.close() # <<<- \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440/\u0441\u043e\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430 \u0438\u043c\u0435\u0435\u0442 \u043c\u0435\u0442\u043e\u0434 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438!\n", | |
"\n", | |
" self._tick += 1\n", | |
" \n", | |
" def stop(self):\n", | |
" self._stop_flag = True\n", | |
" \n", | |
" def add(self, task):\n", | |
" if not self._stop_flag:\n", | |
" # \u0435\u0441\u043b\u0438 \u0438\u0434\u0451\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435\n", | |
" if self._tick:\n", | |
" # \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u0440\u0430\u0437\u0443 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f\n", | |
" task.next()\n", | |
" task.send(self)\n", | |
" self._queue.append(task)\n", | |
" return task" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 31 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u042d\u0442\u043e\u0442 \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 \u043e\u0441\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0448\u0442\u0430\u0442\u043d\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 ```close()```, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0443 \u0432\u0441\u0435\u0445 \u0441\u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440 \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440\u043e\u0432.\n", | |
"\u0422.\u043e. \u0442\u0430\u043c, \u0433\u0434\u0435 \u0440\u0430\u043d\u044c\u0448\u0435 \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 **\u043e\u0436\u0438\u0434\u0430\u043b** \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u043e\u0442 \u0437\u0430\u0434\u0430\u0447, \u0437\u0434\u0435\u0441\u044c \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 \u043c\u043e\u0436\u0435\u0442 \u0435\u0451 **\u0437\u0430\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c**!" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"**\u0417\u0430\u0434\u0430\u0447\u0438** \u0434\u043b\u044f **\u043d\u043e\u0432\u043e\u0433\u043e \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440\u0430**:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def three_step(prefix):\n", | |
" try:\n", | |
" sh = (yield)\n", | |
" print prefix, \": start!\"\n", | |
" (yield)\n", | |
" print prefix, \": step 1\"\n", | |
" (yield)\n", | |
" print prefix, \": step 2\"\n", | |
" (yield)\n", | |
" print prefix, \": step 3\"\n", | |
" except GeneratorExit: # \u0442\u0443\u0442 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443\n", | |
" print prefix, \": stop!\"\n", | |
" # \u0442.\u043a. \u043c\u044b \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0438\u043b\u0438 GeneratorExit\n", | |
" # \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0443\u0436\u043d\u043e \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u0432\u043e\u0437\u0431\u0443\u0434\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u043e\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435:\n", | |
" raise StopIteration()" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 32 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def respawn(task, *args, **kwargs):\n", | |
" \"\"\"\n", | |
" \u0417\u0430\u0434\u0430\u0447\u0430, \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0449\u0430\u044f \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u043f\u043e\u0441\u043b\u0435 \u0435\u0451 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f\n", | |
" \"\"\"\n", | |
" try:\n", | |
" t = task(*args, **kwargs)\n", | |
" t.next()\n", | |
" sch = (yield)\n", | |
" t.send(sch)\n", | |
" while True:\n", | |
" try:\n", | |
" t.send((yield))\n", | |
" except StopIteration:\n", | |
" sch.add(respawn(task, *args, **kwargs))\n", | |
" break\n", | |
" except GeneratorExit:\n", | |
" t.close() # \u0442\u0443\u0442 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u043f\u043b\u044b\u0432\u0430\u0435\u0442 \u0441\u0430\u043c\u043e,\n", | |
" raise StopIteration() # \u043d\u043e \u043c\u044b \u043f\u043e\u0434\u0441\u0442\u0440\u0430\u0445\u0443\u0435\u043c\u0441\u044f :)" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 33 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"# \u044d\u0442\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u0445\u043e\u0434\u0430 \u0438 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435 \u043b\u043e\u0432\u0438\u0442 GeneratorExit\n", | |
"def stop_at(tick):\n", | |
" sh = (yield)\n", | |
" while True:\n", | |
" (yield)\n", | |
" if sh.tick >= tick:\n", | |
" sh.stop()\n", | |
" break" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 34 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"**\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043c!**" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"sch = AdvScheduler()\n", | |
"sch.add(three_step('single'))\n", | |
"sch.add(respawn(three_step, 'repeating'))\n", | |
"sch.add(stop_at(10))\n", | |
"sch.run()" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"single : start!\n", | |
"repeating : start!\n", | |
"single : step 1\n", | |
"repeating : step 1\n", | |
"single : step 2\n", | |
"repeating : step 2\n", | |
"single : step 3\n", | |
"repeating : step 3\n", | |
"repeating : start!\n", | |
"repeating : step 1\n", | |
"repeating : step 2\n", | |
"repeating : step 3\n", | |
"repeating : start!\n", | |
"repeating : step 1\n", | |
"repeating : step 2\n", | |
"repeating : step 3\n", | |
"repeating : start!\n", | |
"repeating : step 1\n", | |
"repeating : stop!\n" | |
] | |
} | |
], | |
"prompt_number": 35 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# greenlets, gevent, ..." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"import greenlet" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 36 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"gr = greenlet.greenlet # \u043b\u0435\u043d\u0438\u0432\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 37 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def evens(data, next_g):\n", | |
" while data > 0:\n", | |
" data, next_g = next_g.switch( # \u043c\u0435\u0442\u043e\u0434 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043d\u0430 greenlet, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f\n", | |
" data - 1, # \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0432 \u043f\u0435\u0440\u0432\u044b\u0439 \u0440\u0430\u0437 \u043f\u0440\u0438\u0434\u0443\u0442, \u043a\u0430\u043a \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u0438,\n", | |
" greenlet.getcurrent() # \u0430 \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0431\u0443\u0434\u0443\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043f\u043e\u0442\u043e\u043a\u043e\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f\n", | |
" )\n", | |
" print \"\u0427\u0435\u0442!\"\n", | |
"\n", | |
"def odds(data, next_g):\n", | |
" while data > 0:\n", | |
" data, next_g = next_g.switch(\n", | |
" data - 1,\n", | |
" greenlet.getcurrent()\n", | |
" )\n", | |
" print \"\u041d\u0435\u0447\u0435\u0442!\"\n", | |
" \n", | |
"\n", | |
"g_evens = greenlet.greenlet(evens)\n", | |
"g_odds = greenlet.greenlet(odds)\n", | |
"g_evens.switch(1000001, g_odds) # \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u043f\u0435\u0440\u0432\u044b\u0439 greenlet" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"\u041d\u0435\u0447\u0435\u0442!\n" | |
] | |
} | |
], | |
"prompt_number": 38 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u041f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 **StateMachine** \u0431\u0435\u0437 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0438 \u0438 \u043f\u0435\u0440\u0435\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u0435\u043a\u0430!" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### \u041d\u0430\u043f\u0438\u0448\u0435\u043c \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 \u0437\u0430\u0434\u0430\u0447 \u043d\u0430 greenlets" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def scheduler(tasks, max_ticks=None):\n", | |
" tasks = list(tasks)\n", | |
"\n", | |
" def scheduler_task(parent_tick=None):\n", | |
" parent = greenlet.getcurrent().parent\n", | |
"\n", | |
" tick = 0\n", | |
" while tasks and (max_ticks and tick < max_ticks if max_ticks else True):\n", | |
" for t in tasks[:]:\n", | |
" # \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0432\u0435\u0440\u0445\n", | |
" parent.switch()\n", | |
"\n", | |
" # \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \n", | |
" t.switch(tick)\n", | |
" if t.dead:\n", | |
" tasks.remove(t)\n", | |
"\n", | |
" tick += 1\n", | |
"\n", | |
" # \u0441\u0430\u043c \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 - \u0442\u043e\u0436\u0435 greenlet\n", | |
" sched = gr(scheduler_task)\n", | |
" for t in tasks:\n", | |
" # \u0432\u0441\u0435\u043c greenlets \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440 \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u0435\u043c\n", | |
" t.parent = sched\n", | |
" \n", | |
" return sched" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 39 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"**\u0417\u0430\u0434\u0430\u0447\u0438**" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def task(tick):\n", | |
" # \u0432\u0441\u0435\u0433\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 greenlet, \u0430 \u0443\u0436\u0435 \u0447\u0435\u0440\u0435\u0437 \u043d\u0435\u0433\u043e - \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 greenlet\n", | |
" switch = greenlet.getcurrent().parent.switch\n", | |
"\n", | |
" print \"task: step 1\"\n", | |
" switch()\n", | |
" \n", | |
" print \"task: step 2\"\n", | |
" switch()\n", | |
" \n", | |
" print \"task: step 3\"\n", | |
" switch()\n", | |
" \n", | |
" print \"done!\"\n", | |
"\n", | |
" \n", | |
"def stop_at(n):\n", | |
" def task(tick):\n", | |
" switch = greenlet.getcurrent().parent.switch\n", | |
"\n", | |
" print \"born\\t*<:D\"\n", | |
" while switch() < n:\n", | |
" print \"alive\\t:)\"\n", | |
" print \"dead\\tX(\"\n", | |
"\n", | |
" return task\n", | |
"\n", | |
"\n", | |
"def nop(tick):\n", | |
" switch = greenlet.getcurrent().parent.switch\n", | |
" while True:\n", | |
" try:\n", | |
" tick = switch()\n", | |
" except greenlet.GreenletExit:\n", | |
" print \"Nop killed at %s tick!\" % tick\n", | |
" break" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 40 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"\u0422.\u043a. \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440, \u044d\u0442\u043e **\u0442\u043e\u0436\u0435 greenlet**, \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \"\u043f\u0438\u043d\u0430\u0442\u044c\"" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"def loop(glet):\n", | |
" cnt = 0\n", | |
" glet.switch(cnt)\n", | |
" while glet:\n", | |
" glet.switch(cnt)\n", | |
" cnt += 1" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 41 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"**\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c!**" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"loop(\n", | |
" scheduler([\n", | |
" gr(task),\n", | |
" gr(stop_at(7)),\n", | |
" gr(nop),\n", | |
"\n", | |
" # \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0439 \u0434\u0438\u0441\u043f\u0435\u0442\u0447\u0435\u0440\n", | |
" scheduler([\n", | |
" gr(nop)\n", | |
" ], max_ticks=10)\n", | |
"\n", | |
" ], max_ticks=20)\n", | |
")" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"task: step 1\n", | |
"born\t*<:D\n", | |
"task: step 2\n", | |
"alive\t:)\n", | |
"task: step 3\n", | |
"alive\t:)\n", | |
"done!\n", | |
"alive\t:)\n", | |
"alive\t:)\n", | |
"alive\t:)\n", | |
"alive\t:)\n", | |
"dead\tX(\n", | |
"Nop killed at 9 tick!\n", | |
"Nop killed at 19 tick!\n" | |
] | |
} | |
], | |
"prompt_number": 42 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"**\u0417\u0430\u0434\u0430\u0447\u0430**, \u043a\u0430\u043a **class**" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"class Task(object):\n", | |
"\n", | |
" def _step(self, tick):\n", | |
" pass\n", | |
"\n", | |
" def _stop(self):\n", | |
" pass\n", | |
"\n", | |
" @classmethod\n", | |
" def as_greenlet(cls, *args, **kwargs):\n", | |
" task = cls(*args, **kwargs)\n", | |
" def worker(tick):\n", | |
" switch = greenlet.getcurrent().parent.switch\n", | |
" while True:\n", | |
" try:\n", | |
" if task._step(switch()):\n", | |
" break\n", | |
" except greenlet.GreenletExit:\n", | |
" task._stop()\n", | |
" break\n", | |
"\n", | |
" return greenlet.greenlet(worker)\n", | |
"\n", | |
"\n", | |
"class TickCollector(Task):\n", | |
" def __init__(self):\n", | |
" self._acc = []\n", | |
"\n", | |
" def _step(self, tick):\n", | |
" self._acc.append(tick)\n", | |
"\n", | |
" def _stop(self):\n", | |
" print self._acc" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 43 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": [ | |
"loop(\n", | |
" scheduler([\n", | |
" gr(nop),\n", | |
" TickCollector.as_greenlet()\n", | |
" ], max_ticks=20)\n", | |
")" | |
], | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": [ | |
"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n", | |
"Nop killed at 19 tick!\n" | |
] | |
} | |
], | |
"prompt_number": 44 | |
} | |
], | |
"metadata": {} | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment