競プロ C++ テンプレート思案
今のテンプレ
// template {{{
#include <bits/stdc++.h>
#define overload2(_NULL, _1, _2, name, ...) name
#define rep1(i, n) for (std::decay_t<decltype(n)> i = 0; i < (n); i++)
#define rep2(i, a, b) for (std::decay_t<decltype(b)> i = (a); i < (b); i++)
#define rep(...) overload2(__VA_ARGS__, rep2, rep1)(__VA_ARGS__)
#if __has_include(<debug.hpp>)
# include <debug.hpp>
#else
# define dbg(...) static_cast<void>(0)
#endif
template<class T> [[noreturn]] void drop(const T &x) {
std::cout << x << "\n";
exit(0);
}
template<class T, class U = T> bool chmin(T &a, U &&b) { return b < a ? a = std::forward<U>(b), true : false; }
template<class T, class U = T> bool chmax(T &a, U &&b) { return a < b ? a = std::forward<U>(b), true : false; }
using i64 = std::int64_t;
using usize = std::size_t;
template<class F> struct rec_lambda {
F f;
explicit constexpr rec_lambda(F &&f_): f(std::forward<F>(f_)) {}
template<class... Args> constexpr auto operator()(Args &&...args) const {
return f(*this, std::forward<Args>(args)...);
}
};
__attribute__((constructor)) static void io_setup() noexcept {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(10);
std::cerr << std::fixed << std::setprecision(10);
}
// }}}
using namespace std;
int main() {}
- どうせ Langage Server は Clangd なので GCC から足を洗って完全に Clang ユーザーになったほうが楽とかあるかもしれない。(懸念事項: 入出力)
- <bits/stdc++.h> じゃなくてたくさんヘッダを書き連ねる。
-
using namespace std
問題。ライブラリではつかってないが最近何となく気持ち悪くなってきたけど、少なくとも global を汚すのは気がひける気がしてきた。ref: https://kmyk.github.io/blog/blog/2020/10/25/coding-style-for-competitive-programming - C++20 を使いたいのでバイナリ提出も考えられる。-> C++ を使う理由に JOI, PCK などのためといった理由があるがルールに抵触しそうなので Rust を使うべき。
-
{{{
/}}}
による fold がいいかんじなので長さとかは気にしない。
ほしい物リスト
-
rep
macro or iterator utilities(std::ranges
like なやつ) -
zip
,enumerate
- type alias
-
chmax
/chmin
(そのままでよさげ) - 入出力の wrapper(read syscall を使ってフルスクラッチしてもよい?)
-
rec_lambda
(そのままでよさげ 2) make_vector
-
drop
(なんやかんや ABC - A, B, C では便利) - <bit> に入ってる人々
-
constexpr
(繰り返し二乗法をいれちゃう?)10^n io_setup
template <class T, T Div = 2> constexpr T INF = std::numeric_limits<T>::max() / Div
- ユーザー定義 literal
sort_by_key
- こういうのもアリ? CF みたいな前処理なしクエリに対応しやすそう。
#include <...>
namesapce atr {
using namespace std;
void main() { ... }
}
int main() { atr::main(); }
- 地味だけどよさげ
#define LAMBDA(...) [&]([[maybe_unused]] const auto& _) { return __VA_ARGS__; }
#define LAMBDA2(...) [&](const auto& _1, const auto& _2) { return __VA_ARGS__; }
- かなり挑戦的だけど argio みたいなのもアリ?
- C++ にはユーザー定義 attribute はないけど↑みたいに
main
じゃないところにソースコードを書くようにすればアリかも。入力は厳しいかも。
- C++ にはユーザー定義 attribute はないけど↑みたいに
ユーザー定義 attribute 風 を見つけたが、よく考えれば関数を分けることに加えて C++ の macro の黒魔術をするのは厳しそうなので attribute を使う必要はないな
戻るのは簡単なので基本は攻めていこうと思う。
コンパイラ依存の機能を使うか
よく考えれば別にbits/stdc++.h
で GCC の犬になってもいいかな。
using namespace std;
まあmain
の中だけかな。
#include <bits/stdc++.h>
namespace atr { void main() {}; }
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(10);
std::cerr << std::fixed << std::setprecision(10);
atr::main();
}
namespace atr {
void main() {
using namespace std;
}
} // namespace atr
rep
とりあえず iterator っぽいものを使う
template<class T = size_t> struct rep {
struct itr {
T i, width = 1;
constexpr itr(const T i, const T width = 1) noexcept: i(i), width(width) {}
void operator++() noexcept { i += width; }
constexpr T operator*() const noexcept { return i; }
constexpr bool operator!=(const itr x) const noexcept { return i < x.i; }
};
const itr f, l;
constexpr rep(const T f, const T l = std::numeric_limits<T>::max(), const T width = 1) noexcept
: f(std::min(f, l), width), l(l, width) {}
constexpr auto begin() const noexcept { return f; }
constexpr auto end() const noexcept { return l; }
};
namespace atr {
using i32 = std::int32_t;
using u32 = std::uint32_t;
using i64 = std::int64_t;
using u64 = std::uint64_t;
using usize = std::size_t;
constexpr std::int32_t operator""_i32(unsigned long long n) noexcept { return static_cast<std::int32_t>(n); }
constexpr std::int64_t operator""_i64(unsigned long long n) noexcept { return static_cast<std::int64_t>(n); }
constexpr std::uint32_t operator""_u32(unsigned long long n) noexcept { return static_cast<std::uint32_t>(n); }
constexpr std::uint64_t operator""_u64(unsigned long long n) noexcept { return static_cast<std::uint64_t>(n); }
constexpr std::size_t operator""_z(unsigned long long n) noexcept { return static_cast<usize>(n); }
template<class T, T N = 2> inline constexpr auto INF = std::numeric_limits<T>::max() / N;
template<class T, class U = T> bool chmin(T& a, U&& b) { return b < a ? a = std::forward<U>(b), true : false; }
template<class T, class U = T> bool chmax(T& a, U&& b) { return a < b ? a = std::forward<U>(b), true : false; }
}
天啓が走ったので argio のやつができそう。
C++17
では std::tuple
がいい感じに推論されるって話だったので
return { { a, b }, { c } };
が
a b
c
で出力されるみたいなのができるんじゃないのか。
問題は vector みたいなコンテナだけど。空白区切りか改行区切りかはかなり ad-hoc なイメージがあって、それをどうするか。(セパレータ文字を変数で持てばいい?)
あとは
return {n, v};
はさすがに
n
v_0 v_1 ... v_{n-1}
になってほしい(コンテナの前後は強制改行?)
Rust like に iterator を取り回すのはとりあえずなしかな、C++(おそらく <ranges> についての知識が不可欠、それ以外もないけど)が足りないし本当に使いたくなったら Rust になる。rep
と revrep
だけで。
ref: https://marycore.jp/prog/cpp/pipe-and-tailing-closure
revrep
の実装が意外と嫌な気持ちになるな、T
が unsigned かつ width != 1
だと嬉しくない。一番丸いのは rep(f, l, w) | reversed
みたいな魔法だけどそれは諦めたのでとりあえず w == 1
固定にするか。
あとは上の出力と入力の軽い wrapper
暫定版
// template {{{
#include <bits/stdc++.h>
namespace atr {
// type aliases {{{
using i32 = std::int32_t;
using u32 = std::uint32_t;
using i64 = std::int64_t;
using u64 = std::uint64_t;
using usize = std::size_t;
// }}}
// user defined literals {{{
constexpr std::int32_t operator""_i32(unsigned long long n) noexcept { return static_cast<std::int32_t>(n); }
constexpr std::int64_t operator""_i64(unsigned long long n) noexcept { return static_cast<std::int64_t>(n); }
constexpr std::uint32_t operator""_u32(unsigned long long n) noexcept { return static_cast<std::uint32_t>(n); }
constexpr std::uint64_t operator""_u64(unsigned long long n) noexcept { return static_cast<std::uint64_t>(n); }
constexpr std::size_t operator""_z(unsigned long long n) noexcept { return static_cast<size_t>(n); }
// }}}
// make_vector {{{
/**
* @brief 多次元 vector の作成
* @author えびちゃん
*/
namespace detail {
template<typename Tp, size_t Nb> auto make_vector(std::vector<size_t>& sizes, Tp const& x) {
if constexpr (Nb == 1) {
return std::vector(sizes[0], x);
} else {
size_t size = sizes[Nb - 1];
sizes.pop_back();
return std::vector(size, make_vector<Tp, Nb - 1>(sizes, x));
}
}
} // namespace detail
template<typename Tp, size_t Nb> auto make_vector(size_t const (&sizes)[Nb], Tp const& x = Tp()) {
std::vector<size_t> s(Nb);
for (size_t i = 0; i < Nb; ++i) s[i] = sizes[Nb - i - 1];
return detail::make_vector<Tp, Nb>(s, x);
}
// }}}
// recusive lambda {{{
template<class F> struct rec_lambda {
F f;
explicit constexpr rec_lambda(F&& f): f(std::forward<F>(f)) {}
template<class... Args> constexpr auto operator()(Args&&... args) const { return f(*this, std::forward<Args>(args)...); }
};
// }}}
// math {{{
template<class T, T N = 2> inline constexpr auto INF = std::numeric_limits<T>::max() / N;
template<class T> constexpr std::enable_if_t<std::is_arithmetic_v<T>, T> power(T a, u32 b) {
T ans = 1;
for (; b; b >>= 1) {
if (b & 1) ans *= a;
a *= a;
}
return ans;
}
template<class T> constexpr T TEN(u32 n) { return pow(static_cast<T>(10), n); }
// part of <bit> from C++20
#if __cplusplus <= 201703L
using bit_t = long long;
int countr_zero(bit_t n) { return n == 0 ? 64 : __builtin_ctzll(n); }
int countl_zero(bit_t n) { return n == 0 ? 64 : __builtin_clzll(n); }
int countr_one(bit_t n) { return n == std::numeric_limits<bit_t>::max() ? 64 : countr_zero(compl n); }
int countl_one(bit_t n) { return n == std::numeric_limits<bit_t>::max() ? 64 : countl_zero(compl n); }
constexpr bit_t mask(u32 n) { return (static_cast<bit_t>(1) << n) - 1; }
int popcount(bit_t n) { return __builtin_popcountll(n); }
bool is_pow2(bit_t n) { return (n & (n - 1)) == 0; }
int msb(bit_t n) { return 63 - countl_zero(n); }
bit_t bit_ceil(bit_t n) { return is_pow2(n) ? n : static_cast<bit_t>(1) << msb(n); }
bit_t bit_floor(bit_t n) { return is_pow2(n) ? n : static_cast<bit_t>(1) << (msb(n) + 1); }
#endif
// }}}
// chmax / chmin {{{
template<class T, class U = T> bool chmin(T& a, U&& b) { return b < a ? a = std::forward<U>(b), true : false; }
template<class T, class U = T> bool chmax(T& a, U&& b) { return a < b ? a = std::forward<U>(b), true : false; }
// }}}
// (rev)rep {{{
template<class T = size_t> struct rep {
struct itr {
T i, width;
constexpr itr(const T i, const T width) noexcept: i(i), width(width) {}
void operator++() noexcept { i += width; }
constexpr T operator*() const noexcept { return i; }
constexpr bool operator!=(const itr x) const noexcept { return i < x.i; }
};
const itr f, l;
constexpr rep(const T f, const T l = std::numeric_limits<T>::max(), const T width = 1) noexcept
: f(std::min(f, l), width), l(l, width) {}
constexpr auto begin() const noexcept { return f; }
constexpr auto end() const noexcept { return l; }
constexpr auto collect_vec() noexcept {
std::vector<T> ret{};
for (auto&& i: *this) ret.emplace_back(i);
return ret;
}
};
template<class T = size_t> struct revrep {
struct itr {
T i;
constexpr itr(const T i) noexcept: i(i) {}
void operator++() noexcept { --i; }
constexpr T operator*() const noexcept { return i; }
constexpr bool operator!=(const itr x) const noexcept { return i != x.i; }
};
const itr f, l;
constexpr revrep(const T f, const T l) noexcept: f(l - 1), l(std::min(f, l) - 1) {}
constexpr auto begin() const noexcept { return f; }
constexpr auto end() const noexcept { return l; }
};
// }}}
void main();
} // namespace atr
// main() {{{
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout << std::fixed << std::setprecision(10);
std::cerr << std::fixed << std::setprecision(10);
atr::main();
}
// }}}
namespace atr {
#define LAMBDA(...) [&]([[maybe_unused]] const auto& $) { return __VA_ARGS__; }
#define LAMBDA2(...) [&](const auto& $1, const auto& $2) { return __VA_ARGS__; }
using namespace std;
// }}}
void main() {}
} // namespace atr
入力の wrapper
右辺値を返す in()
と左辺値参照を読むscan()
問題は operator>>(std::istream, T &t)
(あってる?)がないT
。というか実際 std::vector
くらいしかない(modint
もそうっぽいが ABC232-E で人々が事故してたことを鑑みるに別にこのままで良さそう。)ので scan_vec
みたいなのでいい気がしてきた。読んだ時に関数を適用させるパターンも見つけた。
std::pair
もそうだな。グラフも読めるとかなり嬉しさがある。(edge: std::vector<std::pair<usize, usize>>
隣接グラフ / 隣接行列に変換するやつを書いた方が都合いいな、あとは 1-index を直す件)
template<typename T> inline T in() {
T x;
std::cin >> x;
return x;
}
template<size_t I = 0, class Tp> Tp in(Tp tp) {
if constexpr (I < std::tuple_size_v<Tp>) {
std::tuple_element_t<I, Tp> ti;
ti = in<decltype(ti)>();
in<I + 1>(tp);
} else {
return tp;
}
}
でstd::cin
できる型とそれらからなる tuple-like
な型と(以下再帰)は読める。
inline constexpr auto scan = [](auto&... args) { ((std::cin >> args), ...); };
template<class T> T in() {
T x;
scan(x);
return x;
}
template<class T1, class T2, class... Args> std::tuple<T1, T2, Args...> in() {
std::tuple<T1, T2, Args...> tp;
std::apply(scan, tp);
return tp;
}
とりあえずこれでいいかもしれない。だが改善点が二つ。
-
scan
に tuple-like な型を渡せない。(彼らにoperator>>
を実装することで解決するが美しくない)tuple-like な型はそのままインスタンスになってほしい。 - 1-indexed に対応できない。risujiroh さんは
operator--
を適用できるようにしていた。せっかくstd::tuple
なのでstd::apply
すればよさそう。
備忘録 -
make_vector
の最後の引数に function object を渡せるようにすれば入力に使えそう。
argio みたいな出力
main
の中でやるんじゃなくて別途 print
, println
みたいな関数を用意した方が都合よさそう。
要件
これもういいかな、正直そんな便利じゃない気がしてきた。作っても print(ln)
だけ。
template<class, class = std::void_t<>> struct is_printable: std::false_type {};
template<class T> struct is_printable<T, std::void_t<decltype(std::cout << std::declval<T&>())>>: std::true_type {};
template<class, class = std::void_t<>> struct is_readable: std::false_type {};
template<class T> struct is_readable<T, std::void_t<decltype(std::cin >> std::declval<T>())>>: std::true_type {};
発明した(車輪の再開発)
みたま神さまの協力のおかげで欲しいものを作れた。
// Copyright (c) 2021 Mitama Lab | This code is based on code released under the MIT license; https://opensource.org/licenses/MIT
#include <optional>
#include <tuple>
#include <type_traits>
#include <utility>
template<class T, class = void> struct has_type: std::false_type {};
template<class T> struct has_type<T, std::void_t<typename std::decay_t<T>::type>>: std::true_type {};
template<class T, class = void> struct has_value: std::false_type {};
template<class T> struct has_value<T, std::void_t<decltype(std::decay_t<T>::value)>>: std::true_type {};
namespace detail {
template<class, class, class = void> struct is_tuple_like_detail: std::false_type {};
template<class T, std::size_t... I>
struct is_tuple_like_detail<T, std::index_sequence<I...>, std::void_t<decltype(std::get<I>(std::declval<T>()))...>>
: std::conjunction<has_type<std::tuple_element<I, T>>...> {};
template<class Tp> struct is_tuple_like_impl: is_tuple_like_detail<Tp, std::make_index_sequence<std::tuple_size_v<Tp>>> {};
} // namespace detail
template<class T> struct is_tuple_like: std::conjunction<has_value<std::tuple_size<T>>, detail::is_tuple_like_impl<T>> {};
// Copyright (c) 2021 Mitama Lab | This code is based on code released under the ISC license; https://opensource.org/licenses/ISC
#include <iostream>
#include <tuple>
template<class Tup, std::size_t... Idx> inline bool tuple_scan(Tup&, std::index_sequence<Idx...>);
inline auto scan = [](auto&... args) -> bool {
return (
[&] {
if constexpr (is_tuple_like<std::decay_t<decltype(args)>>::value) {
return tuple_scan(args, std::make_index_sequence<std::tuple_size_v<std::decay_t<decltype(args)>>>{});
} else {
return (std::cin >> args).fail();
}
}(),
...);
};
template<class Tup, std::size_t... Idx> inline bool tuple_scan(Tup& tup, std::index_sequence<Idx...>) {
return (scan(std::get<Idx>(tup)) and ...);
}
template<class T> inline auto in() {
T x;
scan(x);
return x;
}
template<class T, class Proj> inline auto in(Proj&& f) {
auto x = in<T>();
f(x);
return x;
}
template<class... Args> std::enable_if_t<sizeof...(Args) >= 2, std::tuple<Args...>> inline in() {
std::tuple<Args...> tp;
std::apply(scan, tp);
return tp;
}
template<class... Args, class Proj> std::enable_if_t<sizeof...(Args) >= 2, std::tuple<Args...>> inline in(Proj&& f) {
auto tp = in<Args...>();
std::apply([f](auto&&... a) { (f(a), ...); }, tp);
return tp;
}
template<class... Args> inline auto offset1() {
return in<Args...>([](auto& x) { --x; });
}
#include <boost/type_index.hpp>
int main() {
const auto a = in<int>();
const auto [b, c] = in<int, int>();
const auto d = in<int>([](auto& x) { --x; });
const auto [e, f] = in<int, int>([](auto& x) { --x; });
const auto g = in<int>([](auto& x) { --x; });
const auto [h, i] = in<int, std::pair<std::pair<int, int>, int>>();
std::cout << boost::typeindex::type_id_with_cvr<decltype(a)>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(b)>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(c)>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(d)>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(e)>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(f)>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(g)>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(h)>().pretty_name() << std::endl;
std::cout << boost::typeindex::type_id_with_cvr<decltype(i)>().pretty_name() << std::endl;
}
namespace atr
の嬉しさがない。やめる。
あとは出力関数だけ!
これでどうだ!という感じ。いま知ったんだけど関数の返り値って decltype(auto)
にすれば変えられるらしいので in
を軽くリファクタしておくか。
constexpr char sep = ' ';
constexpr char eoln = '\n';
template<class T> auto print(T&&) -> void;
template<class Tp, std::size_t... Idx> inline auto tuple_print(const Tp& tp, std::index_sequence<Idx...>) -> void {
(
[&] {
print(std::get<Idx>(tp));
if constexpr (Idx + 1 != std::tuple_size_v<Tp>) print(sep);
}(),
...);
}
template<class T> auto print(T&& x) -> void {
if constexpr (is_printable<T>::value) {
std::cout << x;
} else if constexpr (is_tuple_like<T>::value) {
tuple_print(x, std::make_index_sequence<std::tuple_size_v<T>>());
} else if constexpr (is_iteratable<T>::value) {
for (auto it = std::begin(x); it != std::end(x); ++it) {
print(*it);
if (next(it) != std::end(x)) print(sep);
}
} else {
static_assert(true, "print: got an unexpented type.");
}
}
template<class T, class U, class... Args> auto print(T&& t, U&& u, Args&&... args) {
print(t);
print(sep);
print(u, args...);
}
template<class... Args> auto println(Args&&... args) {
print(std::forward<Args>(args)...);
print(eoln);
}
だん
// Copyright (c) 2021 Mitama Lab | This code is based on code released under the MIT license; https://opensource.org/licenses/MIT
#include <optional>
#include <tuple>
#include <type_traits>
#include <utility>
template<class T, class = void> struct has_type: std::false_type {};
template<class T> struct has_type<T, std::void_t<typename std::decay_t<T>::type>>: std::true_type {};
template<class T, class = void> struct has_value: std::false_type {};
template<class T> struct has_value<T, std::void_t<decltype(std::decay_t<T>::value)>>: std::true_type {};
namespace detail {
template<class, class, class = void> struct is_tuple_like_detail: std::false_type {};
template<class T, std::size_t... I>
struct is_tuple_like_detail<T, std::index_sequence<I...>, std::void_t<decltype(std::get<I>(std::declval<T>()))...>>
: std::conjunction<has_type<std::tuple_element<I, T>>...> {};
template<class Tp> struct is_tuple_like_impl: is_tuple_like_detail<Tp, std::make_index_sequence<std::tuple_size_v<Tp>>> {};
} // namespace detail
template<class T> struct is_tuple_like: std::conjunction<has_value<std::tuple_size<T>>, detail::is_tuple_like_impl<T>> {};
// Copyright (c) 2021 Mitama Lab | This code is based on code released under the ISC license; https://opensource.org/licenses/ISC
#include <iostream>
#include <tuple>
template<class Tup, std::size_t... Idx> inline bool tuple_scan(Tup&, std::index_sequence<Idx...>);
inline auto scan = [](auto&... args) -> bool {
return (
[&] {
if constexpr (is_tuple_like<std::decay_t<decltype(args)>>::value) {
return tuple_scan(args, std::make_index_sequence<std::tuple_size_v<std::decay_t<decltype(args)>>>{});
} else {
return (std::cin >> args).fail();
}
}(),
...);
};
template<class Tup, std::size_t... Idx> inline bool tuple_scan(Tup& tup, std::index_sequence<Idx...>) {
return (scan(std::get<Idx>(tup)) and ...);
}
template<class T, class... Args> decltype(auto) inline in() {
if constexpr (sizeof...(Args) == 0) {
T x;
scan(x);
return x;
} else {
std::tuple<T, Args...> tp;
scan(tp);
return tp;
}
}
template<class T, class... Args, class Proj> inline decltype(auto) in(Proj&& f) {
if constexpr (sizeof...(Args) == 0) {
auto x = in<T>();
f(x);
return x;
} else {
auto tp = in<T, Args...>();
std::apply([f](auto&&... a) { (f(a), ...); }, tp);
return tp;
}
}
template<class... Args> inline auto offset1() {
return in<Args...>([](auto& x) { --x; });
}
なんかこういうことができたのでコード量削減ができそう。in
と Vec
template<typename T, size_t N> auto Vec_impl(std::vector<size_t>& sizes, T& x) {
if constexpr (N == 1) {
if constexpr (std::is_invocable_v<T>) {
std::vector<std::invoke_result_t<T>> ret(sizes[0]);
for (auto it = std::begin(ret); it != std::end(ret); ++it) *it = x();
return ret;
} else {
return std::vector(sizes[0], x);
}
} else {
size_t size = sizes[N - 1];
sizes.pop_back();
return std::vector(size, Vec_impl<T, N - 1>(sizes, x));
}
}
template<typename T, size_t N> auto Vec(const size_t (&sizes)[N], T&& x = T()) {
std::vector<size_t> s(N);
for (size_t i = 0; i < N; ++i) s[i] = sizes[N - i - 1];
return Vec_impl<T, N>(s, x);
}
できたが、ちょっと難儀な点があって、二次元以上で使うと想定した挙動にならない。
たとえば、
1 2 3
4 5 6
を
auto v = Vec({2, 3}, in<int>);
などとしても
v == {{1, 2, 3}, {1, 2, 3}}
になる。それはそうだし直すのも大変そうなので というのは嘘で、C++ はユーザー定義 waring を提供していないっぽい。[[deprecated]]
しておく
zip
を忘れてたが... いかにせむ