久しぶりにPC周りのお買い物
Microsoft Edge (Windows 10 Mobile) のUser-Agent
なかなかすごいことになっています。
どこかの記事(確かスラド辺り)で読んだことありますが、WebKitと同じ挙動を示すようにしたとかなんとか。
Lumia 950で確認しました。
Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Mobile Safari/537.36 Edge/13.10586
例えば以下のような条件判定に一致するようになります。
var ua = navigator.userAgent.toLowerCase(), cond; cond = /android/.test(ua) && /mobile/.test(ua); console.log("Android : " + cond); // "Android : true" cond = /safari/.test(ua); console.log("Safari : " + cond); // "Safari : true"
Linux; U;
が入っていないのが救いですね。
参照: 滅び行くAndroid 標準ブラウザをサポート外にして悩みの種をなくす話 - Qiita
User-Agentでブラウザの判断をせざるを得ない現状も個人的にはどうかと思います。
本当であれば機能面で分けるべきなんですよね。
console.log("Legacy IE : " + (/*@cc_on ! @*/false)); var webkitKeys = Object.keys(window).filter(function(e){ return window.hasOwnProperty(e) && e.indexOf("webkit") !== -1; }); console.log("WebKit : " + (webkitKeys.length > 0));
まぁこれはこれで穴がありますが…。
POH7の問題でSwiftを初めて書いた
Paizaさんは相変わらず変態ですね(褒め言葉)
今回のPOHも例に漏れずぶっ飛んだ設定になっています。
さて、縞ニーソの問題(この文だけですでに病気だ…)ですが、始めは無難にRubyで書いていたのですが、POH7からはSwiftコンパイラも採用されたということでSwiftでも書いてみることにしました。
ちなみにgolfもやっていたようなので、なるべく短く書くようにしました。
Swiftについては今までそこかしこでObjective-Cとの対比で「短く書ける」と紹介されていたエントリをいくつか読んだ程度で、実際に書いたことはなく、また構文も特に勉強していなかったため全く書けない状態での挑戦です。
知っていることと言ったら
var s = "variable string" // var で変数宣言 let c = "const string" // let で定数宣言 // func で関数宣言 func f() { // do something }
くらいでした。
本題
前置きはこの程度にして、設問ですが、
赤と白の等間隔の縞模様をあなたは作ろうとしています。 各色の幅 n 、縞模様全体の長さ m が改行区切りで入力されます。 赤を 'R' 、白を 'W' とした赤から始まる縞模様を出力して下さい。 例えば3 10のような入力の場合RRRWWWRRRWのように出力してください。
というものです。
1回目
最初に書いたコードはこちらです。
// 98bytes var n=Int(readLine()!)!,m=Int(readLine()!),i=0;for;i<m;++i{print(i/n&1==0 ?"R":"W",terminator:"")} // 展開すると var n = Int(readLine()!)!, m = Int(readLine()!)! for var i = 0; i < m; ++i { print(i / n & 1 == 0 ? "R" : "W", terminator: "") }
軽く解説しますと、readLine()
が標準入力から1行読み取る関数で、返り値の型はString?
です。
String?
はOptional<String>
と同じ意味を表し、つまりOptional
型の一つです。
Optional
型は型引数に取る型の値以外にnil
も持てる型です。
SwiftではOptional
型を純粋な型に戻す時!
を後ろに付けます。
そしてInt()
はキャスト用の関数です。返り値の型はなぜかInt?
です。
なのでvar n = Int(readLine()!)!
は標準入力から一行読み取り、読み取った文字列を数字としてn
に代入する、という操作になります。
i / n & 1 == 0
の部分はi / n
が奇数なのか偶数なのかの判断をしています。
素直に考えれば奇数か偶数かの判断はi / n % 2 == 0
、つまり「2で割って余りが0になるかどうか」になるかと思いますが、なぜこれで奇数と偶数を判断できるのか。
&
演算子はビット演算子と呼ばれるものの一つで、AND演算を表します。
細かい説明は省きますが、AND演算では与えられた二つの二進数の同じ桁を比較し、両方のビットが立っている時に1とします。
0000 & 0001 = 0000 // 0 & 1 = 0 0001 & 0001 = 0001 // 1 & 1 = 1 0010 & 0001 = 0000 // 2 & 1 = 0 0011 & 0001 = 0001 // 3 & 1 = 1 0100 & 0001 = 0000 // 4 & 1 = 0 0101 & 0001 = 0001 // 5 & 1 = 1 0110 & 0001 = 0000 // 6 & 1 = 0 ......
…とまぁ、こういうわけです。
別にi / n % 2 == 0
でも問題ないどころか可読性はこちらの方が高いのですが、ビット演算の方がより高速に処理できます*1。
始めはn回ごとに'R'と'W'を切り替える処理をゴリゴリ書いていたのですが、
i = 0 s = "R" m.times do if i == n then s = s == "R" ? "W" : "R" i = 0 end print s i += 1 end
一度このコードで提出してから、整数型を整数型で割ると返ってくる値は小数点以下を切り捨てた整数であることに気づきました。
n回ごとに'R'と'W'が切り替わるのであれば、n*2回ごとに1周するはず。
つまり0からmまでのループ変数i
を取るとき、i / n
が奇数なのか偶数なのかで'R'を出力するのか'W'を出力するのかが一意に決まります。
そういうわけでここでは奇数偶数判定を用いています。
さて、残るは出力ですが、Swift 1.xでは標準出力用の関数としてprint
とprintln
の2つを用意し、print
では末尾に改行を入れず、println
では末尾に改行を入れる、という差を設けていたらしいです。
しかしSwift 2.0になった時にprintln
を廃止し、print
に一本化したようです。
そして改行なしの出力をしたい場合は、第二引数のオプションにterminator: ""
を付けろ、ということになったみたいです。
長かったけどこれで解説とします。全然軽くなかった。
2回目
二度目に提出したのが以下です。
// 88bytes var n=Int(readLine()!)!,s="";(0..<Int(readLine()!)!).map{s+=$0/n&1==0 ?"R":"W"};print(s) // 展開すると var n = Int(readLine()!)!, m = Int(readLine()!)!, s = "" (0..<m).map { s += $0 / n & 1 == 0 ? "R" : "W" } print(s)
基本的には変わってないのですが、Range
を利用しています。
let m = 0, n = 10 // mからnまでの範囲を表す let r1 = m...n // mからnまで(nを含まない)の範囲を表す let r2 = m..<n var a1 = [Int](), a2 = [Int]() for var i in r1 { a1.append(i) } for var i in r2 { a2.append(i) } print(a1) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] print(a2) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ちなみにforEach
メソッドだと長いのでmap
メソッドを使っています。
ブロック内部ではただs
への再代入をしているだけなので返り値がなく、そのことについてコンパイラが警告を出してきますが、コードを短くするためには已む無しなので無視します。
最初はmap
で'R'か'W'を持つ配列にして、最後にjoin
しようと考えていたのですが、
let arr = ["a", "b", "c"] print("".join(arr)) // "abc"
とできたのは、Swift 1.xの頃の話で、Swift 2.0からは
let arr = ["a", "b", "c"] print(arr.joinWithSeparator("")) // "abc"
となってしまい、全然コード量の節約にならなかったため、泣く泣く文字列に追加する方式を取っています。
ちなみにmap
メソッド内部に書かれた$0
はブロックに渡される0番目の仮引数です。
let r = 0...10 let a1 = r.map{ i in i * 2 } let a2 = r.map{ $0 * 2 }
このa1
とa2
は同じ結果が入ります。
こんな感じでコードを短くしていたんですが、私の頭ではこれ以上無理でした。
ランキングでは71bytesが最短らしく、私よりも17bytesも短いです。
もう少し勉強してより短い糖衣構文をいろいろ習得する必要がありそうです。
ところでRubyでは最終的に以下のようなコードを書きました。
# 48bytes n=gets.to_i;gets.to_i.times{|i|print"RW"[i/n&1]} # 展開すると n = gets.to_i m = gets.to_i m.times do |i| print "RW"[i / n & 1] end
Rubyでは文字列に添え字をつけるとslice
メソッドと等価になります。
s = "abcdefghi" p s[2..5] # "cdef" p s.slice(2..5) # "cdef" p s[1, 2] # "bc" p s.slice(1, 2) # "bc"
1をAND演算すると結果が0か1のどちらかのみに絞られることについては上で説明した通りです。
なので"RW"
という2文字の0番目、または1番目を取り出して出力、というコンパクトなコードになります。
【追記】一文字減らせた
# 47bytes n=gets.to_i;gets.to_i.times{|i|print:RW[i/n&1]}
文字列型"RW"
の代わりに、シンボル型:RW
を使いました。
【さらに追記】さらに一文字減らせた
# 46bytes n,m=$<.map(&:to_i);m.times{|i|print:RW[i/n&1]} # パーレンを省略するとさらに一文字 # 45bytes n,m=$<.map &:to_i;m.times{|i|print:RW[i/n&1]}
Rubyの特殊変数$<
を使っています。
これはARGF
のエイリアスで、こいつにEnumerableのメソッドを付けてやると標準入力を行ごとに取ってくるらしいです(よく理解していない)
参照: AtCoderで見かけたRubyショートコーディング術 - Qiita
あと、縞ニーソ問題のSwiftの最短コード数が69bytesになってますね(12月18日23時前現在)。変態どもめ…。
試してみたい方へ
Swiftはここで書いて遊べます。Xcode持ってるならそちらでもいいけど。
SwiftStub: Online Swift Compiler
Rubyはこちらがお勧めです。別にIdeoneでもいいんですが、こちらの方が手軽です。
compile ruby online
月末ギリギリでのmineoへのMNPはやめた方がいい
mineoではMNP転入の手続きは、消費者自身がmineoマイページの所定のフォームにSIMの製造番号の下4桁を入力して完了となります。
その為、必然的にSIMが届くまではMNPを完了させることができません。
mineoで申し込みをしてから受理されるまで1~2日、さらにSIMが発送されてから届くまで2~3日かかります。
申し込んだ後にこのことに気づいて慌ててmineoサポートセンターに問い合わせても、サポートからは倉庫と連絡を取り合う手段がないため、届く前のSIMの製造番号は確認できません。
また申し込みのキャンセルもできず、SIMの受け取り拒否をしたとしても申し込みから15日後には強制的に切り替わってしまいます。
つまり月末に申し込んだ時点で、MNP元の更新月であったとしてもほぼ確実に解約違約金の10260円がかかることが確定します。
IIJmioでは申し込んだ時点で業者側が勝手に切り替えてくれていたので油断していました。*1
今回は勉強代としておとなしく支払うことにします。
くれぐれもMNPは余裕を持って行うように心がけたいです。
BookLiveで今まで使ってきた金額を出すスクリプト書いた
いやー久しぶりにJavaScript書いたわー。
(function(d){var s=d.createElement("script");s.src="//dl.dropboxusercontent.com/u/414379/www/BookLivePaid/script.js";d.body.appendChild(s)})(document)
一時期Amazon.co.jpで使った額を調べるスクリプトが流行ったことがありましたが、アレのBookLive版だと思えば大体あってます。
使い方
- 上のコードをコピーします。
- http://booklive.jp/my/top に移動してBookLiveにログインします。
- すでにログイン済みならトップページで実行しても問題ないはず。
- 開発者コンソール出す。
- 貼り付けてEnter。
- コンソールに結果が出力されます。
動作確認はIE11でしかしていません。
たぶん他でも動くと思いますが、Firefoxさん辺りは実行前に警告文出すかも。
IE/Firefox/Chromeのそれぞれ最新版で正常に動作することを確認しました。
ソース
実際に実行されるスクリプトの中身です。
呼び出すソースはDropboxにアップロードしたものですが、コードは同一です。
main(); function main() { var now = new Date(); var startYear = now.getFullYear(); var startMonth = now.getMonth() + 1; var paid = 0; var least = prompt("何年まで遡りますか?", startYear); if (!/^[0-9]{4}$/.test(least)) { console.warn("半角4文字の西暦で入力してください。"); return; } for (var y = startYear + 1; --y >= least; ) { for (var m = (y === startYear ? startMonth : 12) + 1; --m > 0; ) { paid += monthSum(y, m); console.info("ここまでの累計: " + paid + "円"); } } console.info("合計: " + paid + "円"); } function monthSum(year, month) { var sum = 0; console.info(year + "年" + month + "月のリクエスト中..."); var doc = request("/my/product?year=" + year + "&month=" + month); var nodes = doc.querySelectorAll('[id^="myproduct_display"]'); if (nodes.length !== 0) { sum = [].slice.call(nodes) .map(function(e){ return e.textContent.trim(); }) .filter(function(text){ return text.indexOf("円") === text.length - 1; }) .map(function(price){ return parseInt(price.split(",").join("")); }) .reduce(function(a, b){ return a + b; }); } console.info(sum + "円"); return sum; } function request(url) { var xhr = new XMLHttpRequest(); xhr.open("GET", url, false); xhr.send(null); var range = document.createRange(); return range.createContextualFragment(xhr.responseText); }
ちなみに
私の消費金額は2013年1月から7月12日現在で654,126円らしいです。