生の方が良いけど。

MacBookが壊れたのはあれだ、すっかり馴染んでしまい、あまり気にかけてやらなかったせいで、倦怠期のカップルの女が妙に怒りっぽくなったりするのと同じだ、ということで久しぶりのプレゼントとしてiSkinなる「アメリカ製の多色展開しているコンドームみたいな名前だな」と思ってしまったキーボードカバーをこちらで購入。
iSkin 1
パッケージが格好良いです。
早速、装着。
iSkin 2
US配列専用です。
iSkin 3
この手のキーボードカバー、何回か使ったことがあるのですが、やはりヘニャヘチャ感は否めません。意外と気に入っていたMacBook特有のカチャカチャとしたキータッチがすっかり陰を潜めています。
また、キーが若干高くなることで今のところ、ミスタッチが多くなっています。
それでも「もう、イヤー」というほどイヤではないので、もうしばらく使ってみようと思います。
昔はキータッチにこだわり『Thinkpad大好きっ子』だったのですが、その辺のこだわりはすっかりなくなってしまった今日この頃です。年齢を重ね「俺も丸くなったな」という感じです。

はてなブックマークに一括登録。

先ほどのスクリプトで誤って削除してしまったのを復元するため、涙を拭いながら急いで作成。
お勉強よりも作業速度短縮を優先でHatenaBMモジュールを使用しています。
HatenaBMモジュールはRubyGemsからインストール可能です。

gem install hatenabm

ファイルから読み込んだURLにrubyというタグをつけ、タイトルはこちらによると編集できないようなので、空文字を渡して登録します。

require 'rubygems'
require 'hatenabm'
require 'uri'

hbm = HatenaBM.new(
                   :user => 'username',
                   :pass => 'password'
                   )

File.open('url.txt') do |file|
  file.each do |line|
    next unless URI.split(line).first == 'http' rescue false

    ret = hbm.post(
                   :title => '',
                   :link => line,
                   :tags => 'ruby'
                   )
    print("#{ret.to_s}:#{line}")
  end
end

読み取った文字列がURLかどうかの判定を

next unless URI.split(line).first == 'http' rescue false

このように行っていますが、こちらを参考にさせていただきました。

はてなブックマークから指定されたタグのエントリを削除する。

こちらを参考に作成。

require 'net/http'
require 'digest/sha1'
require 'rexml/document'
require 'time'

$kcode = 'u'

Net::HTTP.version_1_2

def x_wsse(username, password)
    nonce   = Array.new(10){ rand(0x100000000) }.pack('I*')
    created = Time.new.utc.iso8601
    digest  = Digest::SHA1.digest(nonce + created + password)
    wsse    = "UsernameToken Username=#{username}, ",
              "PasswordDigest=#{[digest].pack('m').chomp}, ",
              "Nonce=#{[nonce].pack('m').chomp}, ",
              "Created=#{created}"
    { 'x-wsse' => wsse.to_s }
end

user = 'username'            # はてなのユーザ名
pass = 'password'            # はてなのパスワード
tag = 'tag'                  # 処理対象のタグ
entries = {}

Net::HTTP.start('b.hatena.ne.jp') do |http|
  
  has_next = true
  start = 0

  # 認証ヘッダ取得
  header = x_wsse(user, pass)

  while has_next
    response = http.get(
                        "/atom/feed?tag=#{tag}&of=#{start}",
                        header
                        )

    doc = REXML::Document.new(response.body)

    open('doc.xml', 'w') { |f| f.puts(doc.to_s)}
    
    # 一度に取得できる件数を取得(tagで検索するとページングする)
    per_page_elem = doc.elements['/feed/openSearch:itemsPerPage']
    if per_page_elem
      items_par_page = per_page_elem.text.to_i
    end

    # エントリのIDとURLを取得
    doc.each_element('/feed/entry') do |entry|
      id = entry.elements['id'].text.scan(/\A.*-([0-9]+)\Z/).to_s
      url = entry.elements['link[@rel="related"]'].attributes['href']
      entries[id] = url
    end

    # 次ページがあるかどうかを判定
    has_next = doc.elements['feed/link[@rel="next"]'] ? true : false
    if has_next
      start += items_par_page
    end

  end

  # 削除するエントリをバックアップ
  File.open('backup.txt', 'w') do |file|
    entries.each_value do |url|
      file.puts(url)
      puts url
    end
  end

  # エントリを削除
  entries.each_key do |id|
    http.delete("/atom/edit/#{id}", header)
  end

end

もし使用される方がいた場合の注意点

  • 一応、削除したエントリのURLをバックアップしていますが、復元は手動になるのでお気をつけください
    • 私はテスト中に間違って消してしまい、涙しました
  • tagを空白にすると全件が削除対象となります
  • 処理には結構、時間がかかりますので終了するまで気長にお待ちください
  • スクリプトでエントリを削除した後、ブラウザ上で確認すると削除されていますが、再度、APIからアクセスすると削除されたエントリが取得されます
    • 何故かはよくわからないです
    • 反映されるまでに時間がかかるのでしょうか。

RubyGemsでインストール可能なモジュールを検索。

RubyGemsからリモートでインストール可能なモジュールを検索するには、http://docs.rubygems.org/read/chapter/

gem q -R -n module_name

とするとあるけど、動かない。
面倒だけ仕方がないので、

gem q --remote -n module_name

としていた。

さっき気がついたけど、

gem q -r -n module_name

とすると動く。
今まで、少しだけ時間を無駄にしてたようで悔しい。

価格.comのAPIを使って検索するモジュール。

すでに作られている方がいるけど、お勉強のために自分の今現在持っている知識で書いた。

require 'open-uri'
require 'rexml/document'

module KakakuAPI
  BASE_URL = 'http://api.kakaku.com/Ver1/ItemSearch.asp'

  class KakakuItem
    include Comparable

    attr_reader:product_id
    attr_reader:product_name
    attr_reader:maker_name
    attr_reader:category_name
    attr_reader:pv_ranking
    attr_reader:image_url
    attr_reader:item_page_url
    attr_reader:bbs_page_url
    attr_reader:review_page_url
    attr_reader:lowest_price
    attr_reader:num_of_bbs

    def initialize(result)
      @product_id = result.elements['ProductID'].get_text
      @product_name = result.elements['ProductName'].get_text
      @maker_name = result.elements['MakerName'].get_text
      @category_name = result.elements['CategoryName'].get_text
      @pv_ranking = result.elements['PvRanking'].get_text
      @image_url = result.elements['ImageUrl'].get_text
      @item_page_url = result.elements['ItemPageUrl'].get_text
      @bbs_page_url = result.elements['BbsPageUrl'].get_text
      @review_page_url = result.elements['ReviewPageUrl'].get_text
      @lowest_price = result.elements['LowestPrice'].get_text
      @num_of_bbs  = result.elements['NumOfBbs'].get_text
    end

    def <=>(rakuten_item)
      return self.product_id <=> rakuten_item.product_id
    end
  end

  class KakakuItems < Array
    attr_accessor:num_of_result
  end

  class ItemNotFoundError < Exception; end;
  class TooManyItemRequestedError < Exception; end;
  class InvalidParameterValueError < Exception; end;
  class InternalServerErrorError < Exception; end;
  
  def self.search(keyword, options = nil)

    param = self.create_param(keyword, options)
    url = URI.escape("#{BASE_URL}?#{param}")

    kakaku_items = KakakuItems.new
    open(url) do |result|

      product_info = REXML::Document.new(result.read).root

      # エラー発生
      if product_info.name == 'Error'
        message = product_info.elements['Message'].get_text.to_s
        message.strip!
        case message
        when 'InvalidParameterValue'
          raise(InvalidParameterValueError.new)
        when 'TooManyItemsRequested'
          raise(TooManyItemsRequestedError.new)
        when 'ItemNotFound'
          raise(ItemNotFoundError.new)
        when 'InternalServerError'
          raise(InternalServerError.new)
        else
          raise('aaa')
        end
      end

      num_of_result = product_info.elements['NumOfResult'].get_text

      kakaku_items.num_of_result = num_of_result

      product_info.each_element('Item') do |elem|
        kakaku_item = KakakuItem.new(elem)
        kakaku_items << kakaku_item
      end

    end

    return kakaku_items
  end
  
  private
  def self.create_param(keyword, options = nil)
    if keyword == nil or keyword.empty?
      raise(ArgumentError.new('keyword'))
    end

    param = ""
    param << "Keyword=#{keyword}"

    unless options[:category_group] == nil
      param << "&CategoryGroup=#{options[:category_group]}"
    end

    unless options[:sort_order] == nil
      param << "&SortOrder=#{options[:sort_order]}"
    end

    unless options[:page_num] == nil
      param << "&PageNum=#{options[:page_num]}"
    end

    return param
  end

end

使ってみる。

items = KakakuAPI.search("MacBook", {:category_group => 'Pc'})
items.each do |item|
  puts item.product_name
end

結果。

MacBook 2000/13.3 MA700J/A
MacBook 2000/13.3 MA701J/A
MacBook 1830/13.3 MA254J/A
MacBook Pro 2330/15.4 MA610J/A
MacBook Pro 2160/15.4 MA609J/A

let式。

特定の定義のみで有効な束縛ができる。

main = print $ letTest1 5
       
letTest1 n = let x = n + 1
                 y = n + 2
                 z = n + 3
             in x * y * z

where節と似ているけど、以下の2点が違う。

  1. letは式なのでlet自体にも値を持つ、whereは節なので値を持たない
  2. whereは複数のガードにまたがって有効

使い分けとしては、letの場合は新しい変数を導入するため、whereは関数内で新しい関数を定義するのが一般的。


let式、where節の中で関数の引数と同じ名前の引数を使用した場合

main = print $ letTest1 20 10


letTest1 x y = let y = x * x
               in y - 1

20 × 20 - 1で結果は、

399

つまり、let式内の変数が使用される。このことをシャドウィング(shadowing)という。


let式内で変数を相互に参照することもできる。

main = print $ letTest
letTest = let x = 5
              y = x + 2
          in x * y

結果。

35

上から参照、下から参照のどちらでも問題ない。

main = print $ letTest
letTest = let y = x + 2
              x = 5
          in x * y

結果は同じになる。


パターンでの束縛も可能。

main = print $ letTest

letTest = let(x:xs) = [1,2,3]
          in xs

結果。

[2,3]


ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門

ふつうのHaskellプログラミング ふつうのプログラマのための関数型言語入門