上京エンジニアの葛藤

都会に染まる日々

VS Code で Perl を書くときに関数ジャンプできるようにする方法

はろー。こんにちは。

みなさん五月病ですか?はい、僕は患ってます。

ところで、Perl 書く時のエディタは Vim 派ですか?
それとも Emacs 派ですか?まあだいたい二手に別れますよね。
そんな僕は Vim 派でしたが、最近は VS Code を使っています。
理由は・・・なんとなくです。

VS Code 自体はデフォルトの設定でもわりかし便利なんですが、どうせなら拡張して関数ジャンプとかできるようにしたいなと思ったので、その方法をここに残します。(めっちゃ簡単)

導入手順

  1. 拡張機能 Perl を入れよう
    marketplace.visualstudio.com
  2. ローカルに ctags を入れよう Mac の人は以下
    brew install ctags
    ※Command Line Tools を入れてない人はインストールできないので、以下のコマンドで入れましょう
    xcode-select --install
  3. VS Code 再起動をして完了

簡単ですね。これで対象の関数にカーソルを当てて F12 で関数ジャンプ!!!
※導入の際はくれぐれも自己責任でお願いします!

react.js で keydown イベントで DOM を更新する方法

はろー。こんにちは。

react.js を最近ちょこちょこ触り始めたので、忘れんうちに書きます!

前提としてそもそもライフサイクルとか正しく理解していないし、redux ってなに???っていう状況なのですが、力技でごりごり前に進んでいきます。
書いてるうちに理解深まるやろ!!!って信じてます。

やりたいこと

「特定のキーを押下したタイミングでイベントを発火させて DOM を更新したい」

button タグを onClick で DOM を変更させるなどは、サンプルがたくさん転がっておりなんとなくでできました。
しかし単純に「→」を押下して DOM を変更させるのはどのライフサイクルで実行すればいいか悩みました。

悩んだ末の実装が以下です。

var React            = require('react');
var ReactDOM         = require('react-dom');
var createReactClass = require('create-react-class');

class SlideComponent extends React.Component {
  constructor(props, context, updater) {
    super(props, context, updater);
    this.state = { word : 'hoge' };
  }

  handleKeyDown(e) {
    if (e.keyCode == 39) {
      this.setState({word: 'foo'});
    }
  }

  componentDidMount() {
    window.addEventListener('keydown', this.handleKeyDown.bind(this));
  }

  render() {
    return (
      <h1>{this.state.word}</h1>
    )
  }
}

ReactDOM.render(
  React.createElement(SlideComponent),
  document.getElementById('content')
);

ポイントは window のリサイズなどのイベントを扱いたい場合は componentDidMount() でイベントを登録して使うそうで、今回は keydown のイベントを登録しそれがトリガーで発火させるようにしました。
とても簡単ですね。

おまけ

かの有名なコナミコマンドでイベントを発火させ DOM を更新する
コナミコマンド「↑↑↓↓←→←→BA」

var React            = require('react');
var ReactDOM         = require('react-dom');
var createReactClass = require('create-react-class');

var konamiCommand = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
var input = [];  

class SlideComponent extends React.Component {
  constructor(props, context, updater) {
    super(props, context, updater);
    this.state = { word : 'hoge' };
  }

  handleKeyDown(e) {
    input.push(e.keyCode);
    if (input.toString().indexOf(konamiCommand) >= 0) {
      this.setState({word: 'KONAMI success!!!'});
      input = [];
    }
  }

  componentDidMount() {
    window.addEventListener('keydown', this.handleKeyDown.bind(this));
  }

  render() {
    return (
      <h1>{this.state.word}</h1>
    )
  }
}

ReactDOM.render(
  React.createElement(SlideComponent),
  document.getElementById('content')
);

Perl でバブルソートをしてみる

はろー。こんにちは。

今更ながらソートアルゴリズムを改めて復習しているので、バブルソートPerl でしてみたいと思います。

バブルソートとは

ソートのアルゴリズムの一つ。隣り合う要素の大小を比較しながら整列させること。最悪計算時間がO(n2)と遅いが、アルゴリズムが単純で実装が容易なため、また並列処理との親和性が高いことから、しばしば用いられる。安定な内部ソート。基本交換法、隣接交換法ともいう。(単に交換法と言う場合もある)
Wikipedia : https://ja.wikipedia.org/wiki/%E3%83%90%E3%83%96%E3%83%AB%E3%82%BD%E3%83%BC%E3%83%88

上記のように Wikipedia では解説されているのでわからない人は読んでください。

実際のコード

#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;

my $array = [13, 5, 41, 20, 1];

for (my $i = 0; $i < scalar @$array - 1; $i++) {
    for (my $n = 1; $n < scalar @$array; $n++) {
        if (@$array[$n] < @$array[$n-1]) {
            my $tmp       = @$array[$n-1];
            @$array[$n-1] = @$array[$n];
            @$array[$n]   = $tmp;
        }
    }
}

warn Dumper $array;

1;

しんぷるにできました。

Perl スクリプトで usage を書いてみた

はろー。こんにちは。
最近暖かくなったり寒くなったりよくわからん毎日ですね
しかし体調を崩すことは全くありません。丈夫なもんです

ところで今回は簡単なスクリプトPerl で書いて、コマンドラインから扱いやすいように usage を出力されるようにしてみました

$ grep --help
usage: grep [-abcDEFGHhIiJLlmnOoqRSsUVvwxZ] [-A num] [-B num] [-C[num]]
    [-e pattern] [-f file] [--binary-files=value] [--color=when]
    [--context[=num]] [--directories=action] [--label] [--line-buffered]
    [--null] [pattern] [file ...]

具体的にこんなやつです
Linux コマンドを使う時確認するやつですよね

そこで実装する実際のスクリプトは、Twitter で該当のツイートをリツイートしているユーザーの情報を標準出力するスクリプトを書きました
実装は以下です

実装

#!/usr/bin/perl
use strict;
use warnings;
use feature qw( say state );

use Net::Twitter;
use Getopt::Long qw(:config posix_default no_ignore_case gnu_compat);
use Data::Validator;
use Encode qw ( encode_utf8 );

my $tweet_id;
my $print_key;
my $help;
GetOptions('i=i' => \$tweet_id, 'p=s' => \$print_key, h => \$help);
if ($help) {
    die show_help();
}
validate( tweet_id => $tweet_id, print_key => $print_key );

my $account = {
    consumer_key        => '%CONSUMER_KEY%',
    consumer_secret     => '%CONSUMER_SECRET%',
    access_token        => '%ACCESS_TOKEN%',
    access_token_secret => '%ACCESS_TOKEN_SECRET%',
};

my $nt = Net::Twitter->new({
    traits   => [qw/API::RESTv1_1/],
    consumer_key        => $account->{consumer_key},
    consumer_secret     => $account->{consumer_secret},
    access_token        => $account->{access_token},
    access_token_secret => $account->{access_token_secret},
});

my $retweet_user_ids = $nt->retweeters_ids({
    id            => $tweet_id,
    stringify_ids => 'true',
});

my $user;
my $retweet_user_list = [];
map { 
    $user = $nt->show_user({ user_id => $_}); 
    push @$retweet_user_list, $user
} @{ $retweet_user_ids->{ids} };

map { say encode_utf8 $_->{$print_key} } @$retweet_user_list;

sub validate {
    eval {
        state $rule = Data::Validator->new(
            tweet_id  => { isa => 'Int' },
            print_key => { isa => 'Str' },
        );
        my $args = $rule->validate(@_);
    };
    if ($@) {
        die show_help();
        exit;
    }
}

sub show_help {
    my $help_doc = <<EOF;
    get retweet info script
    Usage:
        perl $0 [options]
    Options:
        -i : tweet_id (Int)
        -p : print_key (Str)
            ex )
                id, screen_name, location
                https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-users-show            
        -h : help
    Author
        okkun_sh <okkun.sh\@gmail.com> (\@okkun_sh on Twitter)
EOF
    return $help_doc;
}

使用方法

$ perl get_retweet_info.pl -h

    get retweet info script

    Usage:
        perl get_retweet_info.pl [options]

    Options:
        -i : tweet_id (Int)

        -p : print_key (Str)
            ex )
                id, screen_name, location
                https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-users-show

        -h : help


    Author
        okkun_sh <okkun.sh@gmail.com> (@okkun_sh on Twitter)


$perl get_retweet_info.pl -i 1111111111 -p screen_name

-h をすることで usage が出力されるようになります
これでoption -i でツイート id の指定、
-p でリツイートしているユーザーの情報の指定をすればよいということがわかりますね

コードのポイントは、この部分です
GetOptions でオプションの引数の指定をしており、引数に対してバリデーションをかけてエラー時は usage が出力されるようにハンドリングしています
また -h を指定時は usage が出力されるようにもしています

use Getopt::Long;
use Data::Validator;

my $tweet_id;
my $print_key;
my $help;
GetOptions('i=i' => \$tweet_id, 'p=s' => \$print_key, h => \$help);
if ($help) {
    die show_help();
}
validate( tweet_id => $tweet_id, print_key => $print_key );

sub validate {
    eval {
        state $rule = Data::Validator->new(
            tweet_id  => { isa => 'Int' },
            print_key => { isa => 'Str' },
        );
        my $args = $rule->validate(@_);
    };
    if ($@) {
        die show_help();
        exit;
    }
}

sub show_help {
    my $help_doc = <<EOF;
    get retweet info script
    Usage:
        perl $0 [options]
    Options:
        -i : tweet_id (Int)
        -p : print_key (Str)
            ex )
                id, screen_name, location
                https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-users-show            
        -h : help
    Author
        okkun_sh <okkun.sh\@gmail.com> (\@okkun_sh on Twitter)
EOF
    return $help_doc;
}

スクリプト自体はさくっと作ったので動作は怪しいですが、usage の書き方自体は参考になると思います!
完成してから知りましたが、Twitter API ではリツイート情報の取得上限が最大100件らしく全てを取得しきれませんでした。。

もっとシンプルに書ける方法などあれば、コメントください!

Parallel::ForkManager を使って並列処理してみた

ひょんなことから、並列処理が必要とされる場面があったので Parallel::ForkManager とやらを使ってみた

このモジュールを選定した理由は、threads か Parallel::ForkManager で迷ったが、使っている perlbrew では threads が使えなかったため今回は、Parallel::ForkManager を使用しました

ちなみに threads とは、いい感じにマルチスレッドを扱えるクラスです

perldoc.jp

では、Parallel::ForkManager の使い方を書いていきます

導入

cpanm Parallel::ForkManager
いつも通り cpanmコマンドでモジュールを入れちゃいましょう

実装

サンプルコードを載せておきます

#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';

use Parallel::ForkManager;

my $methods = [
    'hoge',
    'foo',
    'bar',
];

my $pm = new Parallel::ForkManager->new(3);
# 子プロセスの最大値を指定し、オブジェクトを生成する

for my $method (@$methods) {
    $pm->start and next;
    # forkする

    if ($method eq 'hoge') {
        &hoge;
    }
    elsif ($method eq 'bar') {
        &bar;
    }
    elsif ($method eq 'foo') {
        &foo;
    }
    $pm->finish;
    # 子プロセスを kill する
}

$pm->wait_all_children;
# 子プロセスの終了を待つ
say "finish";

sub hoge {
    sleep(3);
    say "hoge";
}

sub bar {
    say "bar";
}

sub foo {
    sleep(5);
    say "foo";
}

結果

期待される出力順は、bar → hoge → foo の順ですが・・・

$ perl test.pl
bar
hoge
foo
finish

期待通りの動作でした
こんな感じで簡単にプロセスを fork し並列処理をすることが可能です

metacpan.org

今回は基本的なことしかしていませんが、今後もっと触っていきたいですねー