GitHubのヘッダーを改善する! Google Chrome用拡張 GitHub Better Headerを作りました

最近、GitHubのヘッダーのデザインに変更があり、いくつかのリンクの位置が移動しました。Pull requestやIssuesのリンクはなかなか便利で、これはいい改善なのですが、元々あったリンクでよくクリックしていたものがドロップダウンの中に入ってしまい、使い勝手が悪くなってしまいました。

  • 自分のページ (github.com/ユーザー名) へのリンクがメニューの中に入り、クリックしにくくなった。このページは頻繁に見ていた。
  • Explore がメニューの中に入り、クリックしにくくなった。わりと見ていた。

これをいい感じのヘッダーにするGoogle Chrome拡張を作りました。

GitHub Better Header

GitHub Better Header

めちゃくちゃ便利なので、ぜひ使ってみてください。Web Storeには上げていないので(アイコン作るのがめんどくさすぎて…)、git cloneして直接拡張ページからインストールして下さい。 特徴としては、次のような感じです。

  • ユーザーページ (github.com/ユーザー名) のリンクがドロップダウンメニューではなくヘッダーにある
  • Explore へのリンクがドロップダウンメニューではなくヘッダーにある
  • 設定ボタンがドロップダウンメニューではなくヘッダーにある
  • pure JavaScript (すでのGoogle Chromeに実装されているES6の機能は使っている)
  • ge で Explore を開く
  • gu で ユーザーページを開く

以前のヘッダーがどんな感じだったかだんだん忘れてきていますが、とにかくユーザーページへのリンクが欲しかったという感じなので、今の見た目でかなり満足しています。

実は他にも似たようなプロジェクトはあり、例えば

などがありますが、どちらもDOMが構築されてから書き換えているので、一瞬本来のヘッダーが表示されてから、リンクを挿入されています。見た目がとても悪く、ロードしたあと百ミリ秒ほどしてヘッダーの見た目が変わるのは、体験も悪いです。また、jQueryを同包していたりして、叶えたいこととロードしているスクリプトの量が釣り合っていません。ヘッダーにリンク入れるだけなのにjQuery使うなんてオーバーキルですよね。また、GitHub Old Headerの方はhttps://*/*の権限を要求しており、明らかに拡張の機能が要する権限から逸脱しています。

私の作ったGitHub Better Headerは、"run_at": "document_start"を指定しおりページのロード前からスクリプトがスタートし、MutationObserverを使ってヘッダーの中の要素を入れ替えています。MutationObserverは拡張を作るときに大活躍しますよね。また、入れ替え先のhtmlはそのままリポジトリーに含めているので、ユーザーは好きな様にプロジェクトをフォークし、好きなヘッダーを作ることが出来ます。例えばGistは使わないからリンクを消したいというユーザーもいることでしょう。あるいはPull requestsとかIssuesリンクはいらないよというユーザーもいるでしょう。自由にフォークして好きなヘッダーにして下さい。

GitHubは毎日見るページなので、快適なページであり続けて欲しいです。

AtCoder Regular Contest 040

A - 床塗

'R''C' を数えるだけ。

import Data.Functor ((<$>))

main :: IO ()
main = putStrLn =<< solve <$> (concat <$> tail <$> lines <$> getContents)

solve :: String -> String
solve xs = case length (filter (=='R') xs) `compare` length (filter (=='B') xs) of
  GT -> "TAKAHASHI"
  LT -> "AOKI"
  _ -> "DRAW"

B - 直線塗り

'.' にぶつかったら r 個塗るだけ。最初、ペンキが 'o' にぶつかったらそれ以上は塗られないと思ってたけど違った。提出する前に気がついてよかった。

import Control.Applicative ((<$>), (<*>))

main :: IO ()
main = print =<< solve <$> ((!!1) <$> map read <$> words <$> getLine) <*> getLine

solve :: Int -> String -> Int
solve _ [] = 0
solve r ('o':s) | all (=='o') s = 0
                | all (=='o') $ drop (r - 1) s = 1
                | otherwise = 1 + solve r s
solve r s = 1 + solve r (replicate r 'o' ++ drop r s)

C - Z塗り

最初は全ての (r, c) に対して塗れる数が一番多いやつを選んで…ってやってたけど、TLE/WAしてしまった。よく考えてみると、各行に対して貪欲に調べていくだけでよかった。ある行を全て塗り、かつ次の行もいっぱい塗れるような c を見つけて塗る。そういうふうにして各行に対して塗っていくだけ。

import Data.Functor ((<$>))

main :: IO ()
main = print =<< solve <$> (tail <$> lines <$> getContents)

solve :: [String] -> Int
solve [] = 0
solve xxs@(x:xs) | all (=='o') x = solve xs
                 | otherwise = 1 + solve (paint 0 (last [ i | (i, c) <- zip [0..] x, c /= 'o' ]) xxs)

paint :: Int -> Int -> [String] -> [String]
paint r c xss = [ [ paintOne r c i j x | (j, x) <- zip [0..] xs ] | (i, xs) <- zip [0..] xss ]

paintOne :: Int -> Int -> Int -> Int -> Char -> Char
paintOne r c i j x = if i == r && j <= c || i == r + 1 && j >= c then 'o' else x

jasyさんの解答が美しかった。foldl' で簡単に解けるんだなぁ。ミソは take b ってところかな。うーん、この解法は気が付かなかった。

import Control.Applicative ((<$>), (<*>))
import Data.List (elemIndices, foldl')
import Data.Maybe (fromMaybe, listToMaybe)

main :: IO ()
main = print =<< solve <$> readLn <*> (lines <$> getContents)

solve :: Int -> [String] -> Int
solve n = fst . foldl' (\(a, b) xs -> (a + fromEnum (any (/='o') $ take b xs), fromMaybe n $ listToMaybe $ reverse $ elemIndices '.' $ take b xs)) (0, n)

D - カクカク塗り

わからんぽん。

歩きスマホと横断歩道の見切り横断は本気で危険だと思った

先日、青山にあるオフィスで仕事を終えて、渋谷駅まで向かうため、あたしは青山通りを歩いていました。夜の8時前、あたりはすっかり薄暗くなっていました。とある交差点で信号が赤になり、あたしは横断歩道の少し手前で立ち止まりました。その時、背の低い女子高生があたしの右横を通り過ぎ、スマホの画面を一所懸命覗き込みながら、ふらふらと横断歩道に近づいて歩いて行きました。右折車が数台、目の前を通り過ぎて、右折信号が赤になると、その女子高生はまたふらふらと危なっかしい足つきで車道に足を進め始めました。歩道は赤信号であるにも関わらず、いいえ、それどころか次に青になるのは交差する車道の信号であるにも関わらず、その女子高生はスマホの画面に釘付けになったまま足を進め、横断歩道を半分くらいまで進んでしまいました。そして、交差する車道の信号が青になりました。車道には数台の車が並んでいました。先頭の車の、たしかタクシーだったと思いますが、運転手さんが右手からふらふらと近づいてくる人影に気が付き、クラクションを鳴らしました。ようやく女子高生は状況を把握したようでした。すこし残念そうな仕草をしながら、横断歩道を歩いて戻ってきました。

歩きスマホは危険です。女子高生も、一度や二度くらいは、そのように注意を受けたことがあるはずです。それでも、スマホの画面をずっと見ていたいという気持ちが上回ってしまうのでしょう。その心情も理解できます。スマホに夢中になってしまうのはしょうがないです。いいえ、歩きスマホはとても危険ですしやめて欲しいです。とても危険なんですけど、それとコンボになったときにものすごく危険なのが、横断歩道の見切り横断です。もうすぐ青になるだろう。そういう思い込みで横断歩道を歩き始めるのは、大事故につながりかねません。もしかしたら、本当に女子高生が車に轢かれるのを目にしていたかもしれませんでした。車のクラクションが鳴った時、あたしの頭は真っ白になり、そして直ぐに状況が非常に危険だったことに気が付きました。周りは薄暗く、車の窓枠の陰にその子が隠れて運転手が気が付かなかったら、発進した車に轢かれていた可能性も十分にありました。

しばらくして冷や汗も収まった頃、なぜ女子高生が横断歩道の中央まで歩いてしまうまでに、あたしは声をかけて止めなかったのかということを考えていました。あたしはハッとしました。あの時なんと声を掛けたらよかったのだろう。あぶない?ちょっと待って?とまりなさい?とっさの出来事で声も枯れてうまく引き止められなかったでしょうし、いくら危険回避といっても女子高生に向かっていきなり声をかけることに一瞬のためらいを感じてしまったかもしれません。ここまで考えると、あたしは次第に、あたし自身に何が出来たかということよりも、赤信号を歩いて行く女子高生を止めることができなかった数名の大人たちという構図の怖ろしさについて、考察を始めていました。横断歩道の手前に、あたしを含めて数名が立っていました。なぜ一人として注意を促さなかったのでしょうか。社会というのは弱者を守るものです。赤ちゃんは両親家族はもちろんその存在の周囲の人たちが支えてあげて大きくなっていきます。それが直に中学生、高校生くらいになると、本人に自立の意志が芽生え、周囲の人には干渉されたくないゾーンを持ち始めます。知らない人に声をかけられたらもちろん警戒します。スマホを一所懸命除く様子は、今声をかけるなオーラ全開です。さらに、近年の声かけ事案も、見知らぬ中高生に対して注意を掛けづらくしています。それでも、社会は弱者を守らなくてはいけないはずなのです。

スマホに夢中で赤信号を渡る女子高生を止めなかった大人たちという構図が、もはや現代社会においてありふれたものであるとしたら、それはとても恐ろしいことです。他人と深く関わりたくない、面倒事に巻き込まれたくない、そして自分の身を守るのは己しかいないといった考えが、ある意味で歪んだ形で蔓延する共同体における個々の関係は、弱者を守ることができないほど希薄化してしまっているのかもしれません。

AtCoder Beginner Contest 024

A - 動物園

子供がいくらで大人がいくらなので団体の入場料はという問題。問題文にある通りに計算すればよい。

main :: IO ()
main = getContents >>= print . solve . map read . words

solve :: [Integer] -> Integer
solve [ a, b, c, k, s, t ] = a * s + b * t - if s + t >= k then c * (s + t) else 0

B - 自動ドア

閉まりそうで閉まらない自動ドアの問題。人が立て続けに来たらいつまでも閉まらない。

main :: IO ()
main = getContents >>= print . solve . map read . words

solve :: [Integer] -> Integer
solve (_:t:as) = go 0 0 0 as
  where go n k l (x:xs) | l < x = go (n - k + l) x (x + t) xs
                        | otherwise = go n k (x + t) xs
        go n k l [] = n - k + l

C - 民族大移動

それぞれの民族に対して貪欲に動いていけばいいだけです。

import Control.Applicative ((<$>), (<*>))
import Control.Monad (replicateM)

main :: IO ()
main = getInts >>= \[_, d, k] -> solve <$> replicateM d getInts <*> replicateM k getInts >>= mapM_ print

getInts :: IO [Int]
getInts = map read . words <$> getLine

solve :: [[Int]] -> [[Int]] -> [Int]
solve lrs = map (f lrs)
  where f ([l, r]:rest) [s, t] | s < t && l <= s && r < t = 1 + f rest [max s r, t]
                               | s < t && l <= s = 1
                               | s < t = 1 + f rest [s, t]
                               | s == t = 0
                               | t < s && s <= r && t < l = 1 + f rest [min s l, t]
                               | t < s && s <= r = 1
                               | t < s = 1 + f rest [s, t]
        f _ _ = 0

D - 動的計画法

コンテスト中は解けなかった。id:kmjp様の解答が美しすぎたのでそのままHaskellにしてみた。Pythonpow()相当の関数を用意しなくてはいけないけど、この解法はかなりシンプル。moduloに惑わされず、まずは普通に式で表して、それから演算をmoduloのものにすればいいんですね。こういうのをちゃっかり解けるようになりたい。

main :: IO ()
main = getContents >>= putStrLn . unwords . map show . solve . map read . words

solve :: [Integer] -> [Integer]
solve [ a, b, c ] =
  [ (b * c - a * c) * power (b * a - b * c + a * c) (m - 2) m `mod` m
  , (b * c - a * b) * power (c * a - b * c + a * b) (m - 2) m `mod` m ]
  where m = 1000000007

power :: Integer -> Integer -> Integer -> Integer
power k 1 m = k `mod` m
power k n m | even n = (power k (div n 2) m ^ (2 :: Int)) `mod` m
            | otherwise = (power k (div (n - 1) 2) m ^ (2 :: Int) * k) `mod` m

まぁ、Haskell演算子を自分で定義できるのが本当に最高って感じがするし、演算子をきちんとインポート制御できるのはやっぱり最高って感じだ。以下のコードでもACであるし、式を本当にそのままコードにするだけだ。

import Prelude hiding ((*), (/), (^))
import qualified Prelude as P

main :: IO ()
main = getContents >>= putStrLn . unwords . map show . solve . map read . words

solve :: [Integer] -> [Integer]
solve [ a, b, c ] = [ (a / c) / (a / b + a / c - 1) - 1, (a / b) / (a / b + a / c - 1) - 1 ]

m :: Integral a => a
m = 1000000007

(*), (/), (^) :: Integral a => a -> a -> a

infixl 7 *
x * y = x P.* y `mod` m

infixl 7 /
x / y = x * y ^ (m - 2)

infixl 8 ^
_ ^ 0 = 1
k ^ 1 = k `mod` m
k ^ n = (k ^ div n 2) P.^ (2 :: Int) * k P.^ fromEnum (odd n)

映画『名探偵コナン 業火の向日葵』を観てきました

さすが日曜日のお昼ですね、満席でした。場所はMOVIX京都。女性が多かった。七割くらいかなぁ。子供は意外と少なかった気がします。

www.conan-movie.jp

ゴッホの名画「ひまわり」を七枚集め、展覧会を行う

鈴木次郎吉氏はその夢を叶えるため、かつて消失したとされていた「ひまわり」を落札することに成功する。ところがその絵画を載せた飛行機が着陸前に爆発。コナンは「ひまわり」を持って飛ぶ怪盗キッドを発見して追いかけるも、キッドは絵画を空港に置いたまま姿をくらましてしまう。果たしてキッドが飛行機に爆弾を仕掛けるだろうかとコナンは違和感を覚える。絵画を危険に晒した鈴木次郎吉は、他の「ひまわり」の所有者からの信用を失ってしまう。そんな時に、別の「ひまわり」を展示していた新宿の美術館がキッドに狙われることに。そして、まんまとキッドの予告状通り絵画は盗まれてしまう。展覧会のための貸出を条件に、鈴木次郎吉は絵画の奪還を約束し、キッドとの交渉に乗って100億円と交換で絵画の回収に成功する。

― 七枚の「ひまわり」が集められたレイクロック美術館

鈴木財閥の財力によって建設された、厳重なセキュリティの設備を持つ美術館に、いよいよ七枚の「ひまわり」が集められて展覧会が始まった。毛利小五郎ニューヨーク市警のチャーリー警部も加わった最強の精鋭たち、鈴木次郎吉曰く七人のサムライたちが、名画を見張る目を光らせているところ、コナンがキッドの予告状を発見する。既に怪盗キッドは館内に潜り込んでいた!予告状の暗号は何を暗示しているのか。炎の上がる美術館の中、七枚の「ひまわり」は無事なのか。そして怪盗キッドの真の狙いとは一体!

個人的には、少し盛り上がりに欠けていたのと、途中キッドがとても悪いみたいな感じになっていたのがあまり気持ちよくなかったこと、そして真犯人の動機が微妙だったこと、そして鈴木財閥の力をありありと見せられているのがあまり気持ちよくなかったことで、あまり面白いとは思いませんでした。言ってしまえば、テレビでやる二時間スペシャルをぼーっと見ている感じでした。昔のコナンの映画は本当に楽しかった。それとも自分がコナンを楽しめるような体質じゃなくなってきたのかなぁとか思ったりします。楽しめる人は楽しめると思うので、ぜひ映画館へ。