プログラミングとか色々

プログラミングとかについて色々

Powershellでの引数付きAliasを設定するときの小技

環境

$PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.2.0
PSEdition                      Core
GitCommitId                    7.2.0
OS                             Microsoft Windows 10.0.22000
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

内容

引数付きでaliasを設定したいときに

function ls() {
  exa --icons
}

のように設定しlsを入力すると、関数lsが呼ばれるのではなく、あらかじめaliasとして登録されているGet-ChildItemが実行される

function _ls() {
  exa --icons
}
Set-Alias ls _ls

のようにすることで、lsを入力したときにexa --iconsが実行される

お手軽CLIツール作成

お手軽CLIツール作成

この記事は群馬高専アドベントカレンダー2021の19日目です。

自分の好きなRustを使うことでお手軽にハイクオリティなクロスプラットフォームCLIツールが作れるのでこれを機に紹介させていただこうと思います。

自己紹介

4Jのなまちゃです。

浅く広くいろいろなものに手を出して開発しています。 好きな言語はRustで、Rustを使って競技プログラミングを少しやっています。

サンプル

最近作ったツールがあるのでそれをサンプルに説明します。

サンプルのツールを軽く紹介すると、 競プロ用のテンプレートがあらかじめ書かれたファイルをフォルダにしてコンテスト用のファルダを作成するツールです。

サンプルのソースコードはこちらになります。 https://github.com/Namacha411/kyoprofolder

Rustで作る理由

  • パフォーマンスがよく、C、C++と同等の速度で実行できる
  • コンパイル時にほとんどのバグを排除できる
  • パッケージマネージャーが便利で優秀
  • クレート(ライブラリ)が豊富
  • 配布が簡単
  • クロスプラットフォームで動作する

が大きな理由としてあります。

おすすめライブラリ

StructOpt

RustでCLIツールを作るとき、 代表的なライブラリがStructOptです。 めちゃくちゃ便利です。

これを使うと、以下のように構造体を定義しただけで、

#[derive(StructOpt)]
#[structopt(name = "kyopro folder")]
#[structopt(setting(clap::AppSettings::ColoredHelp))]
struct Opt {
    /// Folder name
    folder_name: String,

    /// Programing language
    lang: String,

    /// File names
    srcs: Vec<String>,
}

コマンドライン引数を構造体に従ってパースを行い、 以下のような説明を自動生成してくれます。 引数がマッチしない場合などのエラーも自動的に行ってくれます。

kyopro folder 0.1.0

USAGE:
    kf <folder-name> <lang> [srcs]...

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

ARGS:
    <folder-name>    Folder name
    <lang>           Programing language
    <srcs>...        File names

dirs-rs

クロスプラットフォームで実行できるようにし、 設定ファイルなどの作成・読み込みを行いたいときおすすめなのがこのdirs-rsです。

これを使うとめんどくさいパス周りをOSにあわせて変更してくれるため、 設定ファイルを作りたいといった時でも使用先のOSを考えることなく、 config_dirと書いておけばよく楽です。

READMEにそれぞれのOSごとのリストがあり、 わかりやすくまとめられているため、 何かあった時でも対応しやすくなっています。

Function name Value on Linux/Redox Value on Windows Value on macOS
home_dir Some($HOME) Some({FOLDERID_Profile}) Some($HOME)
cache_dir Some($XDG_CACHE_HOME) or Some($HOME/.cache) Some({FOLDERID_LocalAppData}) Some($HOME/Library/Caches)
config_dir Some($XDG_CONFIG_HOME) or Some($HOME/.config) Some({FOLDERID_RoamingAppData}) Some($HOME/Library/Application Support)

配布

パッケージマネージャーであるcargoを使って、

cargo publish

でできるらしいですが、なんか怖くてやったことないし、若干めんどくさそうなのでこちらは省略します。

そこでおすすめしたいのが、GitHubなどのGitサーバーへの公開です。 こうすることで利用者側は、

cargo install --git <URL>

リポジトリのURLを入力するだけでインストールできるようになります。

最後に

Rustは学習コストが高いとよく言われていますが、 使えるようになるとパッケージマネージャーやライブラリ、コンパイラがとても優秀で便利なため、 多くの恩恵を受けることができます。 今回のサンプルも100行未満で書くことができました。

皆さん、ぜひRustに入門してみませんか?

明日は、sya-riさんの「サーバーサイドについて書きます」です。

abc217 d

わかったのでメモ

問題

atcoder.jp

解法

  • クエリ1の時、切った場所を順序付き集合に入れていく。
  • クエリ2の時、xの右側の切った場所、左側の切った場所を集合から取り出し、差を求めることで長さが得られる。
  • これを繰り返す。

集合から検索し、取り出す実装が分からなかった。 C++ではsetに二分探索(lower_bound, upper_bound)が使えるらしい。

Rustでのsetの二分探索の代用

RustのBTreeSetにはrange関数が実装されている。 これを使ってxより大きい要素、xより小さい要素を取得できる。 (初めて知った。)

doc.rust-lang.org

また、BTreeSetではnext_backを使うことで後ろから要素(最大値)を取り出せる。 罠もあるっぽいので注意する。

maguro.dev - Rust の BTreeSet / BTreeMap で最大値を素早く取得する方法

解答

Submission #25619157 - AtCoder Beginner Contest 217

use proconio::input;
use std::collections::BTreeSet;

fn main() {
    input! {
        l: usize,
        q: usize,
        cx: [(usize, usize); q]
    };

    let mut set = BTreeSet::new();
    set.insert(0);
    set.insert(l);
    for (c, x) in cx {
        match c {
            1 => {
                set.insert(x);
            }
            2 => {
                let l = *set.range(..x).next_back().unwrap();
                let r = *set.range(x..).next().unwrap();
                let ans = r - l;
                println!("{}", ans);
            }
            _ => unreachable!(),
        }
    }
}

【AtCoder】ABC192復習【Rust】

A問題

解法

 100の倍数ごとにご褒美がもらえるため、100で割ったあまりのみを考えれば良い。100から100で割ったあまりを引くことで、次の100の倍数までの数が求まる。

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! { n: isize };

    println!("{}", 100 - n % 100);
}

B問題

解法

 問題文の「先頭から奇数番目の文字が全て英小文字であり、かつ、先頭から偶数番目の文字が全て英大文字である」をそのまま実装する。文字列を読み取り文字に分解し、すべてがこの条件を満たすとき"Yes"を、そうでないとき、"No"を出力する。

注意点

1based-indexと0besed-indexで偶数と奇数が変わる。

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        s: String
    };

    let ans = s
        .chars()
        .zip(1..)
        .all(|(c, i)| (i % 2 == 1 && c.is_lowercase()) || (i % 2 == 0 && c.is_uppercase()));

    println!("{}", if ans { "Yes" } else { "No" });
}

C問題

解法

 問題文の $g_1(x), g_2(x), f(x)$ を実装しfor文で回す。Kが十分小さいので間に合う。計算量 $O(N)$

桁を並び替えるとき、nを10で割ったあまりを求めその後nを10で割った値に更新していくことで、各桁の数字を入れた配列を作ることができる。文字列と相互に変換する方法もできそう。

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        n: isize,
        k: isize
    };

    let f = |mut n: isize| -> isize {
        let mut a = Vec::new();
        while n != 0 {
            a.push(n % 10);
            n /= 10;
        }
        a.sort();
        let g1 = a
            .iter()
            .zip(0..)
            .fold(0, |sum, (x, i)| sum + x * 10isize.pow(i));
        a.reverse();
        let g2 = a
            .iter()
            .zip(0..)
            .fold(0, |sum, (x, i)| sum + x * 10isize.pow(i));
        g1 - g2
    };
    let mut ans = n;
    for _ in 0..k {
        ans = f(ans);
    }

    println!("{}", ans);
}

【AtCoder】ABC183復習&解説【Rust】

A問題

解法

問題文のReLU関数をそのまま実装する

注意点

負の値が与えられることに注意

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! { x: isize }

    println!("{}", if x < 0 { 0 } else { x });
}

B問題

解法

ゴールの座標をx軸に対称な場所に取り直し、スタートとゴールを結ぶ直線とx軸との交点を求めることで答えを求めることができる。 よって答えを求めるための1次関数 $y = ax + b$ の定数 $a, b$ は $$ a = \frac{G_y - S_y}{G_x - S_x} $$ $$ b = S_y - a*S_x $$ ただし、 $G_x$ はマイナスをかけ直した値。

これより、yが0のときのxが答えとるので $$ x = - \frac{b}{a} $$

注意点

  • 少数の誤差
  • 計算時の符号

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        sx: f64,
        sy: f64,
        gx: f64,
        gy: f64,
    }

    let gy = -gy;
    let a = (gy - sy) / (gx - sx);
    let b = sy - a * sx;

    println!("{}", -b / a);
}

C問題

解法

すべての経路を探索するには順列全探索を使う。 制約が、 $2 \le N \le 8$ なので $O(N \times N!)$ でも間に合う。

ソースコード

use itertools::Itertools;
use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        n: usize,
        k: usize,
        t: [[usize; n]; n],
    }

    println!(
        "{}",
        (1..n)
            .permutations(n - 1)
            .filter(|perm| {
                let mut time = t[0][perm[0]] + t[perm[n - 2]][0];
                for i in 0..(n - 2) {
                    time += t[perm[i]][perm[i + 1]];
                }
                time == k
            })
            .count()
    );
}

D問題

解法

いもす法 を使うことで $O(N + max(T_i))$ で解くことができ、間に合う。 時間は $min(S_i)$ から $max(T_i)$ が扱える配列を用意し記録する。

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        n: usize,
        w: usize,
        stp: [(usize, usize, usize); n]
    }

    let mut water = [0; 200000 + 1];
    for (s, t, p) in stp {
        water[s] += p;
        water[t] -= p;
    }
    let mut using = 0;
    for u in water.iter() {
        using += u;
        if using > w {
            println!("No");
            return;
        }
    }
 
    println!("Yes");
}

【AtCoder】ABC184復習&解説【Rust】

rustでBまで 書きかけ

A問題

解法

問題文通りに $ad - bc$ を計算

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        a: isize,
        b: isize,
        c: isize,
        d: isize,
    };

    println!("{}", a * d - b * c);
}

B問題

解法

最初の点数 $x$ を文字列に応じて増減させていく。

注意点

持っている点数が0点のときに不正解となった場合は点数は減らない

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        n: usize,
        x: usize,
        s: String,
    };

    let ans = s.chars().fold(x, |sum, i| {
        if i == 'x' {
            if sum == 0 {
                sum
            } else {
                sum - 1
            }
        } else {
            sum + 1
        }
    });

    println!("{}", ans);
}

【AtCoder】ABC182復習&解説【Rust】

rustでA~C

A問題

解法

現在の数 $B$ にいくつ数を足せば $2A + 100$ になるか、と読むことができる。

よって、 $$ 2A + 100 - B $$ を計算することで求まる。

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        a: isize,
        b: isize,
    }

    println!("{}", 2 * a + 100 - b);
}

B問題

解法

$N, A$ が十分小さいため、全探索をし最も割り切れる $A$ が多い数を調べる。

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        n: usize,
        a: [usize; n],
    }

    println!(
        "{}",
        (2..=1000)
            .max_by_key(|&x| { a.iter().filter(|&y| { y % x == 0 }).count() })
            .unwrap()
    );
}

C問題

解法

bit全探索を行い、3の倍数ができる集合のうち最も要素数の多いものを探す。 制約が $1 \le N \lt 10^{18}$ だが、桁で考えるため18までを考えればよく、 $O(2^{N})$ のビット全探索でも間に合う。

注意点

  • 使う桁数ではなく、消す桁数を求める。
  • 作ることができない(消す桁数と入力の桁数が等しい、使う桁数が0)なら、-1を出力。

ソースコード

use proconio::{fastout, input};

#[fastout]
fn main() {
    input! {
        mut n: usize,
    };

    let mut dig = Vec::<usize>::new();
    while n != 0 {
        dig.push(n % 10);
        n /= 10;
    }
    let ans = (0usize..1 << dig.len())
        .map(|bit| {
            let mut digsum = 0;
            for i in 0..dig.len() {
                if (1 << i) & bit != 0 {
                    digsum += dig[i];
                }
            }
            if digsum % 3 != 0 { return 0; }
            bit.count_ones() as usize
        })
        .max()
        .unwrap();

    println!(
        "{}",
        if ans == 0 {
            -1
        } else {
            (dig.len() - ans) as isize
        }
    );
}