import random as r; r.seed(1337); " ".join([f'{b:02x}' for b in r.randbytes(20)])Wynik:
'03 0d 25 9e 7b e3 ef ec a5 17 84 88 cd b1 ba b5 ff 3c a8 5d'
ekstaza, geniusz, przebłysk, olśnienie, półprawdy, półśrodki, przemilczenia, zaćmienia, głupstwa, kłamstewka, oszustwa, hultajstwo, wyrachowanie, nieprawda, nieobiektywność, niepodważalna prawda, nierówność, nieomylność, słuszność, perfekcja, krnąbrność ... niegodziwość
import random as r; r.seed(1337); " ".join([f'{b:02x}' for b in r.randbytes(20)])Wynik:
'03 0d 25 9e 7b e3 ef ec a5 17 84 88 cd b1 ba b5 ff 3c a8 5d'
import asyncio async def short_task(): print('short_task before') await asyncio.sleep(2) print('short_task after') async def print_task(): print('print_task') async def long_task(): print('long_task before') await asyncio.sleep(5) print('long_task after') async def draw_task(): print('draw_task') def main(): loop = asyncio.get_event_loop() loop.create_task(print_task()) loop.create_task(long_task()) loop.run_until_complete(short_task()) loop.run_until_complete(draw_task()) loop.close() if __name__ == '__main__': main()Program czeka aż zakończą się dwa zadania: short_task i draw_task, wcześniej uruchamiająć long_task. Ponieważ short_task i draw_task kończą się szybciej dostajemy ostrzeżenie, o wciąż działającym long_taks.
print_task long_task before short_task before short_task after draw_task Task was destroyed but it is pending! task: <Task pending coro=<long_task() done, defined at /home/beru/python_asyncio/run_until.py:13> wait_for=<Future pending cb=[<TaskWakeupMethWrapper object at 0x7f47d9b217d0>()]>>Bardziej zaawansowany przykład serwera/czatu. Tutaj mamy do czynienia z trzema rodzajami zdarzeń: uruchomienie serwera (otwarcie portów i nasłuchiwanie), oczekiwanie na tekst na stdio oraz oczekiwanie na nadejście wiadomości od klienta. Samo oczekiwanie na tekst składa się z dwóch zdarzeń: pojawienie się tekstu na stdio i zapisanie go do kolejki, oraz odczytanie, gdy coś w tej kolejce się znajduje.
import sys import asyncio def main(): queue = asyncio.Queue() loop = asyncio.get_event_loop() # Start monitoring the fd file descriptor for read availability and invoke # callback with the specified arguments once fd is available for reading loop.add_reader(sys.stdin, got_stdin_data, queue) fut = loop.create_future() fut.add_done_callback(cancel_all_task) coro = loop.create_server(lambda: Chat(loop, queue, fut), '127.0.0.1', 7777) server = loop.run_until_complete(coro) # Run until Ctrl+C is pressed or loop is stopped try: loop.run_forever() except KeyboardInterrupt: print('[+] Keyboard exception') cancel_all_task() # Stop server. Closing listening sockets it's done asynchronously, so # wait_closed() need to be used to ensure. server.close() loop.run_until_complete(server.wait_closed()) loop.close() def got_stdin_data(queue): loop = asyncio.get_event_loop() loop.create_task(queue.put(sys.stdin.readline())) def cancel_all_task(result=None): print('[+] Cancel all tasks') loop = asyncio.get_event_loop() for task in asyncio.Task.all_tasks(): task.cancel() loop.create_task(stop_loop()) async def stop_loop(): print('[+] Stop loop') loop = asyncio.get_event_loop() loop.stop() class Chat(asyncio.Protocol): def __init__(self, loop, queue, fut): self.loop = loop self.queue = queue self.fut = fut def connection_made(self, transport): peername = transport.get_extra_info('peername') print('[+] Connection from:', peername) self.transport = transport self.loop.create_task(self._wait_for_stdin_data()) def connection_lost(self, exc): print('[+] Connection lost') self.fut.set_result(True) def data_received(self, data): message = data.decode() print('[+] Data received: {!r}'.format(message)) if message.strip() == "exit": self.fut.set_result(True) def _send_reply(self, reply): print('[+] Data send: {!r}'.format(reply)) self.transport.write(reply.encode()) self.loop.create_task(self._wait_for_stdin_data()) async def _wait_for_stdin_data(self): reply = await self.queue.get() self._send_reply(reply) if __name__ == '__main__': main()W celu połączenia się z serwerm:
nc 127.0.0.1 7777Działanie:
[+] Connection from: ('127.0.0.1', 45260) [+] Data received: 'asdf\n' [+] Data received: 'exit\n' [+] Cancel all tasks [+] Stop loop
# Katalog roboczy mkdir ~/opencv_workspace cd ~/opencv_workspace git clone https://github.com/opencv/opencv.git git clone https://github.com/opencv/opencv_contrib.git # Instalacja virualenv dla Python-a. Przyda się numpy i scipy virtualenv -p python3 venv source venv/bin/activate pip install numpy pip install scipy # Konfiguracja za pomocą CMake. # Wszystko co potrzebne do budowania znajdzie się w katalogu build, # a zainstalowane zostanie do katalogu $VIRTUAL_ENV/local/ cd opencv mkdir build cmake -B build/ -D CMAKE_BUILD_TYPE=Debug \ -D OPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules/ \ -D CMAKE_INSTALL_PREFIX=$VIRTUAL_ENV/local/ \ -D PYTHON_EXECUTABLE=$VIRTUAL_ENV/bin/python \ -D PYTHON_PACKAGES_PATH=$VIRTUAL_ENV/lib/python3.7/site-packages \ -D INSTALL_PYTHON_EXAMPLES=ON # Kompilacja i instalacja (do katalogu $VIRTUAL_ENV/local/) cd build make -j4 make installPrzykładowy program
#include <opencv2/opencv.hpp> #include <iostream> int main() { cv::Mat grayImg = cv::imread("color.png", cv::IMREAD_GRAYSCALE); cv::imwrite("gray.png", grayImg); }Kompilacja:
cd ~/opencv_workspace g++ -I./venv/local/include/opencv4 -L./venv/local/lib -Wl,-rpath=./venv/local/lib \ main.cpp \ -lopencv_core \ -lopencv_imgcodecs \ -lopencv_imgprocWynik:
#include <iostream> #include <numeric> #include <execution> #include <vector> using namespace std; int main() { vector<int> vec{1, 2, 3, 4}; int result = std::reduce(std::execution::par, begin(vec), end(vec)); cout << result << endl; }W przypadku gcc (9.2.1 20191008) wymagane było zainstalowanie dodatkowej paczki libtbb-dev (Threading Building Blocks).
$ sudo apt-get install libtbb-dev $ g++ -std=c++17 main.cpp -ltbb $ ./a.out 10
// Dla std::atomic<int> x{0}; // Operacje: ++x; // atomowy pre-increment x++; // atomowy post-increment x += 1; // atomowy increment int y = x * 2; // atomowy odczyt x x = y + 2; // atomowy zapis do x // Uwaga, ta operacja jest niewspierana x *= 2; // ERROR // Atomowy odczyt x, po którym następuje atomowy zapis do x (dwie operacje) x = x * 2;W przykładzie poniżej, atomic posłużył do blokowania wątków, tak aby funkcje even/odd drukowały naprzemiennie tekst w momencie inkrementacji. Uwaga, nie ma gwarancji, że wartość counter wyświetlana na ekranie będzie zgodna z tym co było sprawdzane w if. Są to dwie atomowe operacje odczytu z pamięci, a wartość counter może się zmienić pomiędzy nimi.
#include <iostream> #include <thread> #include <atomic> using namespace std; std::atomic<int> counter{0}; void odd(size_t n) { for (size_t i = 0; i < n; i++) { if (counter % 2 == 1) { cout << "Odd increment: " << counter << endl; counter++; } else { cout << "Odd check: " << counter << endl; // wartość mogła się zmienić } std::this_thread::sleep_for(std::chrono::milliseconds{20}); } } void even(size_t n) { for (size_t i = 0; i < n; i++) { if (counter % 2 == 0) { cout << "Even increment: " << counter << endl; counter++; } else { cout << "Even check: " << counter << endl; // wartość mogła się zmienić } std::this_thread::sleep_for(std::chrono::milliseconds{40}); } } int main() { constexpr size_t steps{6}; std::thread t1{odd, steps}; std::thread t2{even, steps}; t1.join(); t2.join(); }Wynik:
Odd check: 0 Even increment: 0 Odd increment: 1 Even increment: 2 Odd increment: 3 Odd check: 4 Even increment: 4 Odd increment: 5 Odd check: 6 Even increment: 6 Even check: 7 Even check: 7
#include <iostream> #include <vector> #include <variant> using namespace std; struct Circle { void show() const { cout << "Circle" << endl; } }; struct Rect { void show() const { cout << "Rect" << endl; } }; int main() { using Variant = std::variant<Circle, Rect>; vector<Variant> vec; vec.push_back(Circle{}); vec.push_back(Rect{}); for (const auto& v : vec) { std::visit([](const auto& obj){ obj.show(); }, v); } }Wynik:
Circle Rect
#include <iostream> #include <type_traits> using namespace std; template<typename T> std::enable_if_t<std::is_arithmetic_v<T>, std::string> as_string1(T x) { return std::to_string(x) + " [is_arithmetic_v]"; } template<typename T> std::enable_if_t<std::is_same_v<T, std::string>, std::string> as_string1(T x) { return x + " [is_same_v]"; } template<typename T> std::enable_if_t<!std::is_same_v<T, std::string> && !std::is_arithmetic_v<T>, std::string> as_string1(T x) { return std::string(x) + " [!is_same_v && !is_arithmetic_v]"; } int main() { cout << as_string1(11) << endl; cout << as_string1(std::string("12")) << endl; cout << as_string1("13") << endl; }Wynik:
11 [is_arithmetic_v] 12 [is_same_v] 13 [!is_same_v && !is_arithmetic_v]Inna wersja, gdzie enable_if pojawia się jako parametr szablonowy. Domyślne argumenty szablonowe, nie są częścią sygnatury funkcji szablonowej, trzeba więc do enable_if przekazać jeszcze dummy typ (int), nie wiem czemu trzeba przypisać do tego 0. Dziwny hack.
#include <iostream> #include <type_traits> using namespace std; template<typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0> std::string as_string2(T x) { return std::to_string(x) + " [is_arithmetic_v]"; } template<typename T, std::enable_if_t<std::is_same_v<T, std::string>, int> = 0> std::string as_string2(T x) { return x + " [is_same_v]"; } template<typename T, std::enable_if_t<!std::is_same_v<T, std::string> && !std::is_arithmetic_v<T>, int> = 0> std::string as_string2(T x) { return std::string(x) + " [!is_same_v && !is_arithmetic_v]"; } int main() { cout << as_string2(21) << endl; cout << as_string2(std::string("22")) << endl; cout << as_string2("23") << endl; }Wynik:
21 [is_arithmetic_v] 22 [is_same_v] 23 [!is_same_v && !is_arithmetic_v]Są jeszcze inne formy zapisu enable_if np. jako parametr funkcji, ale w tej chwili nie jest to istotne. W C++17 pojawił się ciekawy mechanizm if constexpr, który pozwala radzić sobie z dużą liczbą takich specjalizacji, a kod wygląda znacznie bardziej czytelnie.
#include <iostream> #include <type_traits> using namespace std; template<typename T> std::string as_string3(T x) { if constexpr(std::is_arithmetic_v<T>) { return std::to_string(x) + " [is_arithmetic_v]"; } else if constexpr(std::is_same_v<T, std::string>) { return x + " [is_same_v]"; } else { return std::string(x) + " [!is_same_v && !is_arithmetic_v]"; } } int main() { cout << as_string3(31) << endl; cout << as_string3(std::string("32")) << endl; cout << as_string3("33") << endl; }Wynik:
31 [is_arithmetic_v] 32 [is_same_v] 33 [!is_same_v && !is_arithmetic_v]
#include <iostream> using namespace std; struct Base { void fun() { printf("Base method\n"); } int value_a; }; struct Derived : public Base { void fun() { printf("Derived method\n"); } int value_b; }; int main() { Base* base = new Base{}; Derived* derived = static_cast<Derived*>(base); // no-error! derived->value_a = 1; derived->value_b = 2; // pisanie po pamięci! cout << derived->value_a << endl; cout << derived->value_b << endl; }Tutaj dopisało nam szczęści, program wykonał się prawidłowo.
$ clang++ -std=c++17 main.cpp $ ./a.out 1 2Sprawa wygląda inaczej, gdy dołączymy address sanitizer.
$ clang++ -std=c++17 -fsanitize=address main.cpp $ ./a.out ==23018==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000014 at pc 0x0000004c63ba bp 0x7fff3301c9b0 sp 0x7fff3301c9a8 WRITE of size 4 at 0x602000000014 thread T0 ...
bool success = init(x); if (success) { cout << "x new value: " << x << endl; }Nowy zapis ze średnikiem (od C++17):
if (bool success = init(x); success) { cout << "x new value: " << x << endl; }Nie widziałem niczego podobnego w innych językach, ale wydaje się całkiem eleganckie. Poniżej przykład z std::map::insert, który zwraca dwie wartości: iterator na element (świeżo wstawiony lub stary o tym samym kluczu) oraz flagę informującą czy wstawienia się powiodło.
#include <iostream> #include <string> #include <map> using namespace std; void insert_to_map(std::map<int, string>& m, std::pair<int, string> p) { if (auto [it, success] = m.insert(p); success) { cout << "success, new elem: " << it->first << " -> " << it->second << endl; } else { cout << "fail, old elem: " << it->first << " -> " << it->second << endl; } } int main() { std::map<int, string> m = { {1, "aaa"} } ; auto b = std::make_pair(2, "bbb"); insert_to_map(m, std::move(b)); auto c = std::make_pair(1, "ccc"); insert_to_map(m, std::move(c)); for (const auto& v : m) { cout << v.first << " " << v.second << endl; } }Wynik:
success, new elem: 2 -> bbb fail, old elem: 1 -> aaa 1 aaa 2 bbb
#include <iostream> #include <valarray> using namespace std; int main() { std::valarray<int> va{1, 2, 3}; va *= 10; for(const auto& v : va) { cout << v << " "; } cout << endl << va.sum() << endl; }Wynik:
10 20 30 60valarray oferuje jeszcze kilka innych klas pomocniczych m.in. std::slice, podobne do tego znanego z Python-a.
for (i = start; i < end; i += step) append(i)Wersja std::slice w C++. Moim zdaniem mechanizm ten jest mniej wygodny i mniej intuicyjny.
for (i = start, j = 0; j < size; i += step, j++) append(i)std::valarray posiada również mechanizm znany z NumPy jako "Indexing with Boolean Arrays". To akurat działa bez zarzutu.
#include <iostream> #include <valarray> using namespace std; int main() { std::valarray<int> va = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; auto sub_va = std::valarray<int>{va[std::slice{0, va.size()/2, 2}]}; for (const auto& v : sub_va) { cout << v << " "; } cout << endl; va[va > 5] = -1; for (const auto& v : va) { cout << v << " "; } }Wynik:
0 2 4 6 8 0 1 2 3 4 5 -1 -1 -1 -1
xterm -tJednym z programów, który korzysta z tych możliwości jest gnuplot. Wszystko co trzeba zrobić to ustawienie w skrypcie terminala jako xterm, vvtek lub tek40xx (zauważyłem też opcję tek410x, ale xterm nie radzi sobie z jej obsługą). Poniżej przerobiony przeze mnie przykład autorstwa Hagen Wierstorf: "Plotting the world revisited". Rożnica, którą ja dostrzegłem to to, że opcja xterm rysuje grafikę w osobnym oknie, vttek, w tym samym w którym uruchomiany jest gnuplot, a tek40xx jest zdecydowanie najszybsze w rysowaniu.
#!/usr/bin/gnuplot # # Plotting the world with the natural earth data set (3D) # # Original AUTHOR: Hagen Wierstorf # Original source: http://www.gnuplotting.org/code/world3d_revisited.gnu # # http://www.gnuplotting.org/plotting-the-world-revisited/ reset # set terminal vttek # set terminal xterm set terminal tek40xx unset key; unset border set tics scale 0 set lmargin screen 0 set bmargin screen 0 set rmargin screen 1 set tmargin screen 1 set format '' set mapping spherical set angles degrees set hidden3d # Set xy-plane to intersect z axis at -1 to avoid an offset between the lowest z # value and the plane set xyplane at -1 set view 56,81 set parametric set isosamples 25 set urange[0:360] set vrange[-90:90] r = 0.99 splot r*cos(v)*cos(u),r*cos(v)*sin(u),r*sin(v) with lines, \ 'world_110m.txt' with linesA prezentuje się to tak:
mkdir vpython3 cd vpython3/ virtualenv -p python3 venv source venv/bin/activate pip3 install jupyter pip3 install vpythonW API pojawiły się pewne drobne zmiany, dlatego postanowiłem przepisać standardowy przykład "bouncing ball" na nową wersję. Przy okazji odkryłem na stronie projektu informacje o module povexport do przechwytywania obrazu animacji. Wcześniej przechwytywałem obraz z ekranu (za pomocą PIL) i wycinałem obszar w którym pojawia się okno animacji.
import vpython as vp import povexport scene = vp.canvas(title='3D scene', x=0, y=0, width=400, height=400, center=vp.vector(0, 0, -0), autoscale=False) vp.display(scene) starting_height = 4 floor = vp.box(pos=vp.vector(0, 0, 0), length=4, height=0.5, width=4, color=vp.color.green) ball = vp.sphere(pos=vp.vector(0, starting_height, 0), radius=1, color=vp.color.red) ball.velocity = vp.vector(0, -1, 0) dt = 0.01 frame = 0 while 1: vp.rate(100) ball.pos = ball.pos + ball.velocity*dt if ball.pos.y < ball.radius: ball.velocity.y = abs(ball.velocity.y) else: ball.velocity.y = ball.velocity.y - 9.8*dt file_name = 'img-%04d.pov' % frame inclist = ['colors.inc', 'stones.inc', 'woods.inc', 'metals.inc'] povexport.export(scene, filename=file_name, include_list=inclist) frame += 1 if ball.pos.y > starting_height: breakCo się zmieniło (dla mnie):
# ... # cpos = 1.5*displayscale*canv.camera.pos # 1.5 is a not understood fudge factor cpos = 10*displayscale*canv.camera.pos # ...W zasadzie, można pokusić się również o stworzenie skryptu, który edytuje pliki .pov i zmienia linijki z ustawieniami kamery. Zmiana w pliku .pov, którą uzyskałem:
camera { right <-image_width/image_height, 0, 0> // vpython uses right-handed coord. system location <0.000000, 0.000000, 173.205081> up <0.000000, 1.000000, 0.000000> look_at <0.000000, 0.000000, 0.000000> angle 60.000000 }Przerobienie plików z formatu .pov na .png:
for i in *.pov; do povray Height=400 Width=400 Display=false Output_File_Type=N $i 2>/dev/null; doneNowa wersja skryptu ([1], [2]) do tworzenia animacji video/gif. Wyeliminowałem straty, które pojawiały się podczas składaniu video z plików .png, przez zastosowania kodeka H.264 i manipulacji "Constant Rate Factor" (uwaga, z ustawieniami CRF trzeba ostrożnie)
#!/bin/sh PALETTE_FILE="tmp_pallete.png" VIDEO_FILE="output_video.mp4" INPUT_FILES="img-%4d.png" OUTPUT_FILE="output.gif" FILTERS="fps=25" ffmpeg -r 100 -i $INPUT_FILES -c:v libx264 -crf 0 -preset veryslow $VIDEO_FILE ffmpeg -v warning -i $VIDEO_FILE -vf "$FILTERS,palettegen" -y $PALETTE_FILE ffmpeg -v warning -i $VIDEO_FILE -i $PALETTE_FILE -lavfi "$FILTERS [x]; [x][1:v] paletteuse" -y $OUTPUT_FILEEfekt końcowy:
$ sudo vim /etc/fstab /dev/sr0 /media/cdrom0 udf,iso9660 user,noauto,exec 0 0Ostatecznie system w takiej konfiguracji liczy sobie około 1.5 GB.
Filesystem Size Used Avail Use% Mounted on udev 2.0G 0 2.0G 0% /dev tmpfs 395M 5.4M 390M 2% /run /dev/sda1 3.9G 1.5G 2.3G 39% / tmpfs 2.0G 0 2.0G 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup /dev/sr0 56M 56M 0 100% /media/cdrom0 tmpfs 395M 0 395M 0% /run/user/1000Pewnie można znaleźć coś mniejszego, ale tak wersja (Debian, którego można rozbudować o nowe pakiety), wydaje cię całkiem ciekawą piaskownicą do dalszego eksperymentowania.