「class」を含む日記 RSS

はてなキーワード: classとは

2008-10-18

real street angels から mechanize を使って動画を取ってきてEmacsで見てみるよ

せっかく書いたから匿名でのせてみるよ

使い方は

  • 動画を取ってきたいよ
    • config.yamlユーザとかを設定するよ
    • ids.txt に取ってきたいIDを書くよ
    • sangels.bat を実行するよ
  • Emacs動画を見たいよ
    • sangels.el を load するよ
    • M-x sangels だよ

必要なものを gem で取ってくるにはこうすればいいよ

  • gem install -r log4r
  • gem install -r -v 0.6 hpricot
  • gem install -r mechanize

長すぎてelispが消えたから続きがあるよ

sangels.bat - 起動用バッチファイル

@echo off
setlocal
set WD=%~dp0
cd /d %WD%

ruby get_movies.rb
ruby get_images.rb
ruby create_m3u.rb

ruby

config.yaml - 設定ファイル
user: ユーザID
password: パスワード
ids_file: ids.txt
done_file: ids_done.txt
movies_dir: movies

log4r_config:
  pre_config:
    global: INFO
  loggers:
    - name: app
      type: Log4r::Logger
      level: INFO
      outputters:
        - STDOUT
        - FILE
  outputters:
    - name: STDOUT
      type: Log4r::StdoutOutputter
      formatter:
        type: Log4r::PatternFormatter
        pattern: "%d [%l] %C - %M"
        date_pattern: "%H:%M:%S"
    - name: FILE
      type: Log4r::FileOutputter 
      filename: "#{LOGDIR}/sangels.log"
      formatter:
        type: Log4r::PatternFormatter
        pattern: "%d [%l] %C - %M"
        date_pattern: "%Y-%m-%d %H:%M:%S"
get_movies.rb
require 'fileutils'
require 'logger'
require 'mechanize'

BASEDIR = File.dirname($0)
require "#{BASEDIR}/util"
require "#{BASEDIR}/sangels"

$config = load_config(BASEDIR)
prepare_logger(BASEDIR)
$log = new_logger("get_movies")
WWW::Mechanize.log = new_logger("mechanize")
WGet.log = $log

class IDFile
  def initialize(file)
    @file = file
    unless File.exist?(@file)
      Fileutils.touch(@file)
    end
  end

  def ids(contains_comment = nil)
    File.open(@file) {|io|
      io.to_a.map {|x|
        x.chomp
      }.select {|x|
        if x.empty?
          nil
        elsif contains_comment
          true
        else
          not /^\s*\#/ =~ x
        end
      }
    }
  end

  def add(id)
    ids = ids(true)
    unless ids.any? {|x| x == id}
      write(ids + [id])
    end
  end

  def delete(id)
    ids = ids(true)
    if ids.any? {|x| x == id}
      write(ids - [id])
    end
  end

  def write(ids)
    File.open(@file, "w") {|io|
      ids.each {|x| io.puts x}
    }
  end
end


$log.info("BEGIN #{$0} ================")
exit_code = 0
begin
  ids_file = IDFile.new($config.ids_file)
  done_file = IDFile.new($config.done_file)
  movies_dir = $config.movies_dir
  wget = WGet.new

  sangels = SAngels.new
  sangels.login($config.user, $config.password)
  ids_file.ids.each {|id|
    begin
      movies = sangels.movies(id)
    rescue SAngels::Movies::InvalidMoviesError
      $log.warn("invalid movie id: #{id}")
      next
    end
    dir = File.expand_path(id, movies_dir)
    movies.each {|link|
      wget.retrieve(link.href, dir)
    }
    expected = movies.movie_links.map{|x| File.basename(x.href)}
    actual = Dir.glob("#{dir}/*").map {|x| File.basename(x)}
    if (expected - actual).empty?
      done_file.add(id)
      ids_file.delete(id)
    end
  }
rescue => e
  $log.error(e)
  exit_code = 1
end

$log.info("END #{$0} (#{exit_code}) ================")
exit exit_code
get_images.rb
require 'fileutils'
require 'logger'
require 'mechanize'
require 'ostruct'

BASEDIR = File.dirname($0)
require "#{BASEDIR}/util"
require "#{BASEDIR}/sangels"

$config = load_config(BASEDIR)
prepare_logger(BASEDIR)
$log = new_logger("get_images")
WWW::Mechanize.log = new_logger("mechanize")
WGet.log = $log

$log.info("BEGIN #{$0} ================")
exit_code = 0
begin
  movies_dir = $config.movies_dir
  sangels = SAngels.new
  sangels.login($config.user, $config.password)
  thumbnails = sangels.thumbnails

  Dir.glob("#{movies_dir}/*").each {|dir|
    next unless File.directory? dir
    id = File.basename(dir)

    url = thumbnails.url(id)
    unless url
      $log.warn("#{id} is not found")
      next
    end
    path = File.expand_path("00_thumbnail#{File.extname(url)}", dir)
    next if File.exist? path

    $log.info("retrieving #{url}")
    thumbnail = thumbnails.get_file(id)
    File.open(path, "wb") {|io| io.write(thumbnail)}
  }
rescue => e
  $log.error(e)
  exit_code = 1
end

$log.info("END #{$0} (#{exit_code}) ================")
exit exit_code
create_m3u.rb
BASEDIR = File.dirname($0)
require "#{BASEDIR}/util"

$config = load_config(BASEDIR)
movies_dir = $config.movies_dir
Dir.glob("#{movies_dir}/*") {|dir|
  next unless File.directory? dir
  name = File.basename(dir)
  files = Dir.glob("#{dir}/*.wmv").sort

  File.open("#{movies_dir}/#{name}.m3u", "w") {|io|
    files.each {|file|
      io.puts "#{name}/#{File.basename(file)}"
    }
  }

  File.open("#{dir}/00_movies.m3u", "w") {|io|
    files.each {|file|
      io.puts "#{File.basename(file)}"
    }
  }
}
sangels.rb
require 'mechanize'
require 'hpricot'

BASEDIR = File.dirname($0)
require "#{BASEDIR}/util"

class SAngels
  HOST = "real2.s-angels.com"
  LOGIN_URL = "http://#{HOST}/member/"
  INFO_URL = "http://#{HOST}/teigaku/item.php"
  THUMBNAILS_URL = "http://#{HOST}/teigaku/"
  THUMBNAIL_URL = "http://#{HOST}/images/default/thumb/"

  def initialize()
    @agent = WWW::Mechanize.new
  end

  def login(user, password)
    login_form = @agent.get(LOGIN_URL).forms.find {|form|
      form.fields.any? {|field| field.name == "frmLoginid"}
    }
    login_form.frmLoginid = user
    login_form.frmPw = password
    @agent.submit(login_form)
  end

  def movies(id, no_validate = nil)
    Movies.new(@agent, id, !no_validate)
  end

  def thumbnails
    Thumbnails.new(@agent)
  end

  class Thumbnails
    def initialize(agent)
      @agent = agent
      doc = Hpricot(@agent.get_file(THUMBNAILS_URL))
      elems = doc.search("div[@class=realthum]/a")
      @links = Hash(
        elems.map {|elem|
          href = elem["href"]
          id = $1 if /ID=(.+)/ =~ href
          url = elem.search("img")[0]["src"]
          [id, url]
        })
    end

    def get_file(id)
      @agent.get_file(url(id))
    end

    def url(id)
      @links[id]
    end

    def exist?(id)
      url(id)
    end
  end

  class Movies
    class InvalidMoviesError < StandardError
    end

    def initialize(agent, id, no_validate)
      @agent = agent
      @id = id
      if !no_validate &amp;&amp; !valid?
        raise InvalidMoviesError
      end
    end

    def info_page_url
      "#{INFO_URL}?ID=#{@id}"
    end

    def info_page
      @agent.get(info_page_url)
    end

    def movies_page
      @agent.click(info_page.links.find {|link| /P=10/ =~ link.href})
    end

    def movie_links
      movies_page.links.select {|link|
        /wmv$/ =~ link.href
      }.sort {|a, b|
        File.basename(a.href) <=> File.basename(b.href)
      }
    end

    def valid?
      info_page.uri.to_s == info_page_url
    end

    def each(&amp;block)
      orig_links = movie_links
      orig_links.each {|orig_link|
        link = movie_links.find {|l| File.basename(l.href) == File.basename(orig_link.href)}
        block.call(link)
      }
    end
  end
end
util.rb
require 'log4r'
require 'log4r/yamlconfigurator'
require 'singleton'
require 'fileutils'
require 'ostruct'

def Hash(a)
  Hash[*a.flatten]
end

def load_config(basedir)
  OpenStruct.new(File.open("#{basedir}/config.yaml") {|io| YAML.load(io)})
end

def new_logger(name)
  Log4r::Logger.new("app::#{name}")
end

def prepare_logger(basedir, logdir = nil)
  logdir ||= basedir
  Log4r::YamlConfigurator["LOGDIR"] = logdir
  Log4r::YamlConfigurator.load_yaml_file("#{basedir}/config.yaml")
end

class NullObject
  include Singleton
  def method_missing(message, *arg)
    NullObject.singleton
  end
end

class WGet
  class << self
    attr_accessor :log
    def initialize
      super
      @log = NullObject.singleton
    end
  end
  
  def log
    self.class.log
  end

  def retrieve(url, dir)
    FileUtils.mkdir_p(dir)
    file = File.expand_path(File.basename(url), dir)
    if File.exist?(file)
      log.info("already retrieved #{url}")
      return true
    end

    tmp = "#{file}.part"
    log.info("retrieving #{url}")
    ret = system("wget", "-c", "-O", tmp, url)
    if ret
      log.info("retrieving succeeded #{url}")
      File.rename(tmp, file)
    else
      if $? == 0x020000 # Ctrl-C
        exit($?)
      else
        log.error("retrieving failure #{url} (#{$?})")
      end
    end
    return ret
  end
end

2008-10-10

http://anond.hatelabo.jp/20081010220640

どう表現したいか、によるんじゃないかな。

重要なのはあくまでリストだってことであれば、ULマークアップするのはアリ。各LIの中に情報書き込めばいいんじゃないかな。

ただ、TABLE要素が不適だとも思わないけどね。

たとえば、名前の列と生年月日の列があれば、社員同士を比べてどっちが年上かなどを把握しやす論理構造になる。TABLEってのは視覚的に把握しやすくなるだけじゃなく、「ここには名前情報が入る」「ここには生年月日情報が入る」って定義できるわけで、その点ではhttp://anond.hatelabo.jp/20081002194359で書いてあるULプロフィールマークアップするよりも意味のある論理構造になるんじゃないかな。

もちろん、LI要素にclass属性つけて「何の情報か」を付け加えてもいいけれど、単にLI要素の中にテキストで「○年○月○日」と書き込んであっても、それが「生年月日」であるか「入社日」であるかはわからないわけだ。

だから、俺は社員「リスト」だけれどもTABLEでマークアップしてもいいと思う。

2008-09-14

60行で作らないPHPテンプレートエンジン

今更だけど60行で作るPHP用テンプレートエンジンクラス化した。

  1. PHP5で動かしてね
  2. 細かい使い方はコード見て適当に判断してね
  3. もちろん改変、商用利用自由にね
  4. サンプルテンプレート60行で作るPHP用テンプレートエンジンと一緒
コード
<?php
class NoSixTemplate{
   protected $template_dir = null;
   protected $template = null;
   protected $context = array();
   
   function __construct($filename = null, $directory = null){
      $this->set_template($filename);
      $this->set_dir($directory);
   }
   
   function set_template($filename){
      $this->template = $filename;
   }
   
   function set_dir($directory){
      if(substr($directory, -1) != '/') $directory .= '/';
      $this->template_dir = $directory;
   }
   
   function set_data($context_key, $context_data, $overwrite = false){
      if(empty($this->context[$context_key]) || $overwrite){
         $this->context[$context_key] = $context_data;
      }else{
         if(is_array($context_data) &amp;&amp; is_array($this->context[$context_key])){
            $this->context[$context_key] = array_merge($this->context[$context_key], $context_data);
         }else{
            $this->context[$context_key] .= $context_data;
         }
      }
   }
   
   function reset($context_key = null){
      if(is_null($context_key)){
         $this->context = array();
      }else{
         $this->context[$context_key] = null;
      }
   }
   
   function convert_template(){
       $filename = $this->template_dir.$this->template;
       $cachename = $filename.'.cache';
       if(!file_exists($cachename) || filemtime($cachename) < filemtime($filename)){
           $s = file_get_contents($filename);
           $s = $this->convert_string($s);
           
           if(is_writable($cachename) || is_writable($this->template_dir)){
               file_put_contents($cachename, $s);
           }
       }
       return $cachename;
   }
   
   function convert_string($s) {
       $s = preg_replace('/^<\?xml/', '<<?php ?>?xml', $s);
       $s = preg_replace('/#\{(.*?)\}/', '<?php echo $1; ?>', $s);
       $s = preg_replace('/%\{(.*?)\}/', '<?php echo htmlspecialchars($1,ENT_QUOTES); ?>', $s);
       return $s;
   }
   
   function display(){
       $cache = $this->convert_template();
       extract($this->context);
       include($cache);
   }
}
?>
サンプルコード
<?php
require_once('NoSixTemplate.php');
$template = new NoSixTemplate('template.php', 'template_directory');
$template->set_data('title', 'Example');
$template->set_data('list', array(10,'<A&amp;B>',NULL));
$template->display();
?>

2008-08-06

pythonダメな1つの理由

1. if/while/for/class 文に : (コロン)が必要

例)

while i<5:
    i=i+1
    print i

python はインデント、改行に意味を持たせることをウリとして開発されたわけで、

ここにはインデントがあり、それに意味を持たせられるのだから、: コロンなど必要なはずがない。

Why are colons required for the if/while/def/class statements?

http://www.python.org/doc/faq/general/#why-are-colons-required-for-the-if-while-def-class-statements

(意訳)ボブ「比べてみろよ?こっち

if a==b
    print a

と、こっち

if a==b:
    print a

どっちが読みやすい、スザンヌ?」

スザンヌ「まぁ、2番目のほうが読みやすいわ。」

んなぁことあるかーい!!変わらんわい!!

(追記)ボブ「あとは、英語の文法に似せたってのもあるんだ。」

: (コロン)の後に説明書くにしても10行は書かんだろがーー!!それに、似せたんなら、

if a==b,
    print a

のほうが英語っぽいだろがー!! ってか printprint a. (ピリオド) って書けやそれなら!

中途半端中途半端

2008-07-31

全部 public でいい!

よく、クラスのメンバ変数を private にして、setter と getter 関数を作れといいますよね。こんな風に。

class Person {
    private $_name;
    public function setName($name)
    {
        if (empty($name)) {
            throw new IllegalArgumentException();
        }
        $this->_name = $name;
    }
    public function name()
    {
        return $this->_name;
    }
}

でも、明らかにバカっぽい!! public なら1行じゃないか!!

誰しも、1度ならず10度ぐらいは考えたことがあると思います。

我々崇高なるプログラマはこんなコードを書くために時間を費やしていいはずがない。

そこで、私はあえて、もう全部 public でいい!と推します。関数も。

上記がメインの理由ですが、他の理由もこねくりだしてみます。

なんでこの関数が private !?

オープンソースプロジェクトコードを見ていて、

中々便利そうな関数があるじゃないか、と思ったら private ! なんてことが実は結構あります。

(それを正式に使いたいとなったら、パッチを投げて議論を交わし導入されるのを待たなければいけません。)

コードコミットした人は上級者といっていいプログラミングレベルの人です。

そんな人でも、private にしたほうが意図せず外からアクセスされなくて安心、の理論で、

とりあえず private 関数にしてしまうのです。なぜか?

考えたくないからです。

他から利用されたりすることが完全にないと言い切れるのか、などと悩みたくないからです。

この作業は単純なようでいておそろしく時間がかかります。

ありとあらゆる可能性をつぶしていかなければならないからです。

「あとはコードに直すだけ」

なんて言葉を上級者から聞いたことはありませんか?

彼らにとっては、本当に時間がかかるのはアルゴリズムだったり、このようなコード設計なのです。

このコード設計の時間を短縮できれば彼らの崇高なる脳力を有効活用できるのです。

もう、全部 public にすればいいじゃん!

この private 変数覗きたいんですけど!

確かに、設計上、その変数は他所から覗かれることはない変数でしょう。

でも、デバグ目的だったりで、メンバ変数の値を覗きたい、なんていうことは良くあります。

UnitTest をやっていて、途中経過の値を見たい、なんて実はよくあるんじゃないですか?皆さん。

UnitTest は関数仕様テストするもので、途中経過がどんな値であろうか問題ではない?

知るか!!オレが今やりたいのはデバグなんだよ!!

なんて実はよくあるんじゃないですか?皆さん。

そんな時、メンバ変数が private だと、わざわざリフレクションを使ったトリックを使ってアクセスしたりと、

本来必要のない時間の使い方をしちゃったりします。

我々崇高なるプログラマはこんなコードを書くために時間を費やしていいはずがない。

2008-07-07

もん毛スター for firefox

動作未確認。すんげー冗談半分。

// Hatena Monge Star user script
// 2008-07-07
// by masda. (http://anond.hatelabo.jp/20080707043247)

// ==UserScript==
// @name           Hatena Monge Star
// @namespace      http://anond.hatelabo.jp/20080707043247
// @description    Hatena Monge Star
// @include        http://b.hatena.ne.jp/entry/*
// @version        0.3.1
// ==/UserScript==

// deriving from [http://d.hatena.ne.jp/Hamachiya2/20080707/HatenaBlackStar2] ver Firefox
//               [http://f.hatena.ne.jp/hatenacinnamon/20070109001332]
// Thx! and CUTE!


location.href = 'javascript:(' + function() { (function (w) {

	if (typeof(w.Ten) == 'undefined') {
		return;
	}

	HatenaBookmarkMongeStar = new Ten.Class({
		initialize: function(li, entryTitle) {
			var comment = '';
			var tags    = '';
			var commentSpans = Ten.DOM.getElementsByTagAndClassName('span', 'comment', li);
			if (commentSpans.length > 0) {
				comment = Ten.DOM.scrapeText(commentSpans[0]);
			}

	        var tagsSpans = Ten.DOM.getElementsByTagAndClassName('span', 'user-tag', li);
			if (tagsSpans.length > 0) {
				$A(tagsSpans[0].getElementsByTagName('a')).each(function(a) {
					tags += '[' + Ten.DOM.scrapeText(a)+ ']';
				});
			}

			var title = tags + comment;
			if (title.length == 0) {
				var name = Ten.DOM.scrapeText(li.getElementsByTagName('a')[1]);
				title = name + 'のブックマーク';
	        }

			// this.uri   = 'http://b.hatena.ne.jp/keyword/' + li.getElementsByTagName('a')[1].href;
			var u = li.getElementsByTagName('a')[1].href;

			if (u.indexOf('#') == -1) {
				this.uri = u + '#_HatenaMongeStar';
			} else {
				this.uri = u + '_HatenaMongeStar';
			}

			this.title = title + ' - ' + entryTitle;

			this.comment_container = Hatena.Star.EntryLoader.createCommentContainer();
			var target = commentSpans[0] || li;
			target.appendChild(this.comment_container);

			this.star_container = Hatena.Star.EntryLoader.createStarContainer();
			this.star_container.className = 'hatena-star-star-container MongeStarContainer';
			target.appendChild(this.star_container);
		}
	});


	var tryCount = 0;
	var tryMax = 300;
	function waitForHatenaStar() {
//		if (Hatena.Star.EntryLoader.loaded) {
//			Hatena.Star.EntryLoader.loaded = false;
		var s = document.getElementsByClassName('hatena-star-add-button');
		if (s.length) {

			Hatena.Star.EntryLoader.loaded = false;

			Hatena.Star.EntryLoader.loadEntries = function() {
				var entries = [];
				var title = Ten.DOM.scrapeText(Ten.DOM.getElementsByTagAndClassName('span', 'title', document.body)[0]);
				var ul = document.getElementById('bookmarked_user');
				if (ul) {
					$A(ul.getElementsByTagName('li')).each(function(li) {
						if (li.className != 'more') {
							entries.push(new HatenaBookmarkMongeStar(li, title));
						}
					});
				}
				return entries;
			}
			new Hatena.Star.EntryLoader();

		} else {
			if (++tryCount > tryMax) {
				setTimeout(waitForHatenaStar, 400);
			}
		}
	}

	setTimeout(waitForHatenaStar, 500);

})(window); }.toString() + ')()';



GM_addStyle(<><![CDATA[

	.MongeStarContainer {
		margin-left: 4px;
	}

	.MongeStarContainer .hatena-star-add-button {
		background-color: #fc6 ! important;
	}

	.MongeStarContainer a {
		text-decoration: none ! important;
		color: #f80 ! important;
		font-size: 10px;
		position: relative;
	}

	.MongeStarContainer a:before {
		content: '毛';
		font-size:small;
	}

	.MongeStarContainer a .hatena-star-star {
		filter: alpha(opacity=00);
		-moz-opacity:0.00;
		opacity:0.00;

		position: absolute;
		top: 0;
		left: 0;
	}

	.MongeStarContainer .hatena-star-inner-count {
		color: #f90 ! important;
	}

]]></>);

動いたらいいな-

2008-07-02

javascript継承方法の決め手

やっぱりprototype.jsClass.create?

使った事無いけどスーパークラスのメソッド呼び出しできるんだっけ?

http://iandeth.dyndns.org/mt/ian/archives/000664.html

とか参考に自分で書いたら段々でかくなって、4行が今57行w

でもまだバグあり機能不足。絶対車輪の再発明してる。

やっぱ言語サポートないと辛いわ。こんなに大変だと思わなかった。

って事で「javascript継承方法の決め手」なーい?と聞いてみる。

欲しいのは、多重継承が可能で、オーバーイドした子クラスのメソッドから、簡単にオーバーイドされてる親クラスのメソッド呼びたい。ただし、thisは子クラスオブジェクトのままで、しかも何段でもチェーンをたどれるように。

それとも、あきらめて、クラス名付きで直接親メソッド呼んだ方が良いのかな。やっぱり。

ま。とりあえずシュミグラマだから趣味に走ってみるけれども。

2008-06-21

[]もういつでもどこでもだれでもMooseでいいじゃねぇか

おれはもうMooseしかつかわねぇ。後にも先にもMooseMooseMooseMooseMoose!!!!!!!!!!!1111111

ってな人の為にいつでもどこでもMooseする。automooseを実装しますた


package automoose;
use strict;
use warnings;

sub import {
    strict->import;
    warnings->import;
}

package automoose::before;
use Moose; no Moose;

package automoose::after;
use Moose;

my @before  = keys %automoose::before::;
my @after   = keys %automoose::after::;
my @exports = do { my %u; @u{@before} = (); grep { !exists $u{$_} } @after };

package UNIVERSAL;
use Moose;

for my $func (@exports) {
    __PACKAGE__->meta->remove_method($func);
    __PACKAGE__->meta->add_method($func,sub {
        my $class = shift;
        my $auto  = $class.'::__auto__';
        no warnings 'redefine';
        local *Moose::_get_caller = sub { return $class };
        Moose->import( { into => $auto } );
        my $code = $auto->can($func);
        $class->meta->add_method($func,sub {
            shift;
            goto $code;
        });
        goto $code;
    });
}

1;

使い方はいたって簡単。useするだけ。


use automoose;

my $obj = Foo->new;

いきなりnewが呼べちゃう。

他にも


use automoose;

Foo->has( hoge => is => 'rw' ,default => 9999 );
Foo->has( muge => is => 'rw' ,default => 7777 );

print Foo->new->hoge;
print Foo->new->muge;
Bar->extends('Foo');

print Bar->new->hoge;

ょーかんたん。げーべんり。

しっかしこれ、automooseだけど実装するの結構めんどかったのよ。Moose-0.44をベースに作ったんだけどさ。

Moose内部で使用している$CALLERって変数レキシカルなもんだから、どうやってそれを外から制御すればいいのかすんごい苦労したわけさね。

で結局importの引数にinto渡してさらにMoose::_get_caller関数を上書き無理矢理ハックしたってわけさ。

でもね。でもね。でもね。ちょっと聞いてよ。

ふと最新のMoose-0.50見てみたらさ、Moose::__CURRY_EXPORTS_FOR_CLASS__なんて関数定義されてるわけよ。

外から明示的に$CLASSを変更できるインターフェイスなわけよ。おいおいおいおい、勘弁してくれよ。こっちゃ折角苦労してハックしたのにあっさり公式対応するなってばよ。メゲルヨ?ぼく。

まぢめげるよ。めげる。ってかもうめげたよ。もうMooseなんてつかわんね!つかわんね!

Mooseなんて大嫌いだー!

俺はMooooooooooseをやめるぞぉおおおおおおおおお、JOJOぉぉぉおおおおお!!!!11

プログラ増田のあなぐら

2008-06-05

[][]Moose?Mooseってなんだ?あれか?整髪料か?え?Perl

最近Perl界隈ではMoose、MooseってなんかMooseってのが流行ってるらしい。

もう完全に出遅れてしまったので増田で書き殴ってみる。

自分自身のブログでは、さもずっと前からMoose知ってたかのように振舞うために、増田で先に放出しておく。てへへ。

プログラマ層が限りなく低い増田にこんなこと書いてもだれも見てくれない気はするけど。

初めてのMoose - Mooseのすすめ - はてな#hide-k

初めてのMoose

meta object protocol について考えてみる - TokuLog 改めChumbyとどきました日記

YappoLogs: Moose のコードを探索して理解を深めた

Mooseってのは結局のところClass::MOPのラッパーみたいなもんだと。

で、Class::MOPってのは何だ?ってことだけど、メタなんとかプログラミング?え?プロトコル?まーどっちでもいい。

よくよく読んでいくとメタなんとかとか大層な名前が付いてるけど、結局のところPerlのpackageそのものの操作をオブジェクティブ扱えるようにしたものみたいだ。

つまりだな、例えばpackageに対して動的に(静的ではなく!)メソッドを追加したい場合、今までなら


package Foo;

**Foo::method = sub {
	return 'hoge';
};

print Foo->method;

のように型グロブに関数リファレンスを突っ込むということをしなければなかったが


use Class::MOP;

my $class = Class::MOP::Class->create('Foo');

$class->add_method('method',sub {
	return 'hoge';
});

print Foo->method;

みたいな感じでかっこよく追加できるってわけさ。ま、これはほんの一例だけどな。(他にもメソッドを削除したりフックしたり色々できる。その辺は今回省略。)

本来なら「package Foo」とするところを「my $class = Class::MOP::Class->create('Foo');」と書ける。

これの何が良いのかというと、$classというオブジェクト経由でFooパッケージを色々操作できるところにつきる。

型グロブを使用したり「no (warnings|strict)」をしたりパッケージを操作する処理っていうのはPerlのキチャナイ構文が多かったのだが、Class::MOPのおかげでスッキリ綺麗に書けるようになったってこった。

で、次にMooseだが、これは結局のところClass::MOPのパッケージ管理の部分に+αしただけのラッパーだ。

でもその+αってのが結構凄かったりする。

もうこの辺の話はさんざん既出だが、例えばhasという関数を使ってアクセサや型定義が出来たり


package Foo;
use Moose;

has 'method' => ( is => 'rw', isa => 'Int' , default => '10' );

my $obj = Foo->new;

print $obj->method;   # 10

$obj->method(50);

print $obj->method; # 50

$obj->method('hoge') # Int型じゃないのでエラー

Moose::Roleを使ってRubyのMixinみたいなことができたりする。

でも実はこれらの処理ってのは本当は別に凄くもなんとも無い。

アクセサ生成なんてClass::Accessorがあるし、関数引数の型チェックなんてのもParams::Validate等昔から存在してるし、Mixinに関してはもともとPerlは多重継承できるので最初からできるし。

じゃあなんでみんなMoose、Moose言ってるのかっていうと、それはやはりClass::MOPの存在が大きいであろう。

綺麗且つ柔軟にパッケージの操作が出来るClass::MOPが土台にあって、今まで別々の役割として存在してきたモジュール達を統合し、よりわかりやすく、より柔軟に、そしてより強力なPerlオブジェクト指向を構築できるようにした。それがMooseなのだ。



・・・しかし、小生。

Mooseについて調べていくうちに一つ残念に思ったことがある。

オブジェクトにメソッドを追加する機構がないのだ。

オブジェクトにメソッドを追加する、だ。パッケージにではなく、オブジェクトに、だ。

具体例をあげる。


package Foo;
use Moose;

my $obj = Foo->new;
$obj->meta->add_method('hoge', sub { return 'hoge' });

print $obj->hoge; # hoge

ちなみに$obj->metaというのはFooパッケージを管理するClass::MOPへのアクセサだ。

ということは上記の処理はFooに対してhogeというメソッドを追加していることになる。

では次の例。


package Foo;
use Moose;

my $obj = Foo->new;
$obj->meta->add_method('hoge', sub { return 'hoge' });

print $obj->hoge; # hoge

my $obj_2 = Foo->new;
print $obj_2->hoge; # hoge

$obj_2->hogeが呼べてしまうわけだ。

$obj->metaは結局のところFooパッケージなのだから、そこにメソッドを追加しているので当然の結果である。

$objだけにメソッドを追加することは、Mooseではできないのだ。

非常に残念である。ああ、残念だ。




・・・しかし、小生。

これでもプログラマの端くれである。こんなことでめげていてはMooserを名乗れないのである。(あ、MooserってのはMoose使いの人の俗称ね。今僕が考えたの)

なのでオブジェクトにメソッドを追加できるように拡張して見せよう。


package Foo;
use Moose;

use Class::Object;
my $class_object = Class::Object->can('new');
override new => sub { ref($class_object->(shift))->SUPER::new(@_) };

my $obj = Foo->new;
$obj->meta->add_method('hoge', sub { return 'hoge' });

print $obj->hoge; # hoge

my $obj_2 = Foo->new;
print $obj_2->hoge; # エラー

たった3行追加するだけで実現できる。さすがMoose。

ただし、Class::Objectを利用しているのでFoo->newで返ってくるパッケージがFoo::0といったようにFooではなくなってしまっているのでrefとかでパッケージ名の比較ができなくなってしまう問題が発生する。

でもこれも継承順をいじったりと本気で頑張れば、表向きに見せるパッケージ名をFooすることも可能だろう。

その添削の役目はどこかのハッカーに任せるとして、今日のところはこの辺で終了としたい。

Moooooooooooooose!と叫ぶのが流行ってるみたいなので、もっとも長くMooooooooooooooose!と叫んだ最初の男となるべく下記の処理を残しておく。


length q chdir uc and print chr ord uc q rmdir and do { print chr ord q xor x while $a++ < 0xffffffff } or print chr ord qw q sin q and print chr ord q ne sin and print chr hex length q q shift shmread bless q;





プログラ増田のあなぐら

2008-05-13

GuiceSeasar2 を連携させてみたよ

[参考文献]

    S2 は、.dicon ファイルで定義をだいぶ簡略化できる。パフォーマンスはどうなんだろう。誰かテストしてくれいw

    app.dicon

    <?xml version="1.0" encoding="UTF-8" ?>

    <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" "http://www.seasar.org/dtd/components24.dtd">

    <components namespace="client">

    <include path="hello.dicon" />

    <component class="org.seasar.guice.Client" />

    </components>

    hello.dicon

    <?xml version="1.0" encoding="UTF-8" ?>

    <!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN"

    "http://www.seasar.org/dtd/components24.dtd">

    <components initializeOnCreate="false">

    <component class="org.seasar.guice.HelloServiceImpl" />

    </components>

    HelloService.java、HelloServiceImpl.java は、上記 ITPro と内容が同じなので省略。

    Module.java

    package org.seasar.guice;

    import org.seasar.framework.container.S2Container;

    import org.seasar.framework.container.SingletonS2Container;

    import org.seasar.framework.container.factory.S2ContainerFactory;

    import com.google.inject.AbstractModule;

    import com.google.inject.name.Names;

    public class Module extends AbstractModule {

    S2Container container = null;

    public Module(S2Container container){

    this.container = container;

    }

    @Override

    protected void configure() {

    bind(S2ContainerFactory.class).annotatedWith(Names.named(container.getPath()));

    bind(Client.class).toInstance(SingletonS2Container.getComponent(Client.class));

    }

    }

    Client.java

    package org.seasar.guice;

    public class Client {

    private HelloService helloService = null;

    public void setHelloService(HelloService helloService) {

    this.helloService = helloService;

    }

    public void execute() {

    helloService.sayHello();

    }

    }

    Main.java

    package org.seasar.guice;

    import org.seasar.framework.container.S2Container;

    import org.seasar.framework.container.factory.SingletonS2ContainerFactory;

    import com.google.inject.Guice;

    import com.google.inject.Injector;

    public class Main {

    private static final String PATH = ".\\app.dicon";

    public static void main(String[] args) {

    SingletonS2ContainerFactory.setConfigPath(PATH);

    SingletonS2ContainerFactory.init();

    S2Container container = SingletonS2ContainerFactory.getContainer();

    Module module = new Module(container);

    Injector injector = Guice.createInjector(module);

    Client client = injector.getInstance(Client.class);

    client.execute();

    }

    }

    実行結果

    java -cp ?? org.seasar.guice.Main

    2008/05/13 21:19:22 org.seasar.framework.log.Logger info

    情報: Running on [ENV]product, [DEPLOY MODE]Normal Mode

    Hello, world!

    実行環境JDK 7 (build 1.7.0-ea-b24)

    2008-03-24

    http://anond.hatelabo.jp/20080323175904

    [1] http://anond.hatelabo.jp/20080323175904

     と

    [2] http://d.hatena.ne.jp/ryozo18/20080324/1206301703

    を読んで思ったことを書きます。

    [1]の人はお子様ですね。その給料が何のために支払われているのかわかってない。

    金もらって働いている自覚が全然ないんだろうな。

    [2]の人は、ずばりこのことを言いたいんじゃないかなぁ、と思った。

    [1]も、自分でもなさけねーなーと思ってるからこそ匿名で書いているんだろうけど、ちょっと前まで学生年収が600万になろうかとしているくそ坊主にそんな甘っちょろいことを言われたら、どんな人でもぶちぎれると思う。

    僕も同じようなこと考えたことがあるから[1]の気持ちは分かります。

    ちなみに、僕のスペックは、入社3年目で年収は[1]には及ばなくもきっちり貰ってる方だと思っている中小企業プログラマ。入社して早々に大規模プロジェクトに投入されてデスマーチの中を生き残って(?)今日に至る。

    大規模プロジェクトといっても下っ端にできる仕事と行ったら仕様書通りのclassを書くくらいしかないわけで、まわりの面白そうな案件をやってる先輩がとってもうらやましかったんですよ。だから、最初の頃は[1]と同じようなことを考えていました。

    でも、デスマーチの中で働いてるうちに、なんでこんなつらい思いをしなきゃならないのか、もっと楽にできないのか、とか考えだして、自分の関わっている範囲から少しずつ、品質責任を持つようになったんですよね。

    #デスる原因はいろいろあるとは思いますが、やはりPJに関わっている個人の意識が低いことが原因の一つだと思う

    また、何よりもバグを作って大変な目に遭うのは自分だけじゃなくてまわりの先輩やお客さんだということがよくよくわかってくるので、品質をあげるためにより真剣に取り組むようになりました。

    そうやって少しずつプロ意識が芽生えてきたわけなんですが、おそらく[1]はまだ、そういった切羽詰まった状況に追いつめられていないんでしょうね。

    学生のノリでここまできている。自分のことしか考えていない。責任感、皆無ですよね。



    だいたい、2年目にして年収600万ももらってるやつが、

    「自分のやりたいことができないんです。転職した方がいいですかね?」

    なんて悩んでたとしても、それに本気で答えるやつなんていないですよ。自分の親ですら「好きにすれば」っていいますよ。そんなもん。

    勝手にやればいいんですよ。何をぐだぐだいじいじ言ってるのかなぁ。

    それから、会社では自分のやりたいことができないウボァー、といっていますが、会社はあなたの知的好奇心を満足させるために年収600万を用意しているわけではないと思うんですよ。600万で、与えたその仕事をきっちりやってくださいと言ってるんじゃないかなぁ。さらには、600万以上の仕事をしてくれるのを期待しまくっているんだと思う。

    だから、自分のやりたいことを今いる会社でやりたいんなら、その+αの部分で自分のやりたいことを実現するのが筋ってもんでしょう。

    結構な金を貰っときながらあまっちょろいことを口にしている人は、自分が会社の期待に応えているのか見つめ直してみたらいいと思う。プロ意識の低いうちは何してもうまく行かないんじゃないかなーと思います。

    2008-03-22

    []Python 2.6だと、オブジェクト辞書のキーに使えない場合がある

    Python2.5のときは、新スタイルクラスインスタンス辞書のキーとして必ず使用できたけど、Python 2.6ではそういうわけじゃなくなってるぽい。という話。

    class TestClass(object):
        def __init__(self, i_name):
            self._name = i_name
    
        def __eq__(self, i_other):
            if not isinstance(i_other, TestClass):
                return False
            return self._name == i_other._name
    
    print 'object.__hash__ = ' + str(object.__hash__)
    print 'TestClass.__hash__ = ' +str(TestClass.__hash__)
    

    上記のようなコードを実行したら、Python 2.5とPython 2.6で結果が違いました。

    Python 2.5の場合の結果。

    object.__hash__ = <slot wrapper '__hash__' of 'object' objects>
    TestClass.__hash__ = <slot wrapper '__hash__' of 'object' objects>
    

    Python 2.6の場合の結果。

    object.__hash__ = <slot wrapper '__hash__' of 'object' objects>
    TestClass.__hash__ = None
    

    Python 2.6だと、objectクラス継承すると、__hash__がNoneになっちゃってる。このままだと辞書のキーとして使えないね。object継承したクラスで__hash__を実装すれば、大丈夫みたい。

    ハッシュ値の計算方法 (2)」というページも参考になりそう。

    2008-03-14

    [][]はてブhotentryにて2chコピペブログや「ネタ」を削除

    http://anond.hatelabo.jp/20080302214727

    ネタ」がうまくいかない件は、"\u30cd\u30bf"にしたらうまく行った

    あと2chコピペサイトを2つ追加

    とりあえず、Sleipnir2のSeahorseで確認。

    // ==UserScript==
    // @name           hatebufilter
    // @namespace      hatebufilter
    // @description    Hatena bookmark filter
    // @include        http://b.hatena.ne.jp/hotentry*
    // @include        http://b.hatena.ne.jp/entrylist*
    // ==/UserScript==
    /*
    問題点
    いまのところなし
    
    ・問題が起こりそうなURL
    http://b.hatena.ne.jp/hotentry?mode=daily&amp;date=20080224
    http://b.hatena.ne.jp/hotentry?mode=daily&amp;date=20080218
    **/
    
    (function(){
    	// Hatebu Tag
    	var HatebuTagParentNum = 3;
    
    	var filters = [
    		// moconico douga
    		{"tag": "div", "name": "entry", "pattern": "nicovideo\.jp"},
    /*
    		// tag of "2ch"
    		{"tag": "a", "name": "tag", "pattern": "2ch", "parentNum": HatebuTagParentNum},
    		{"tag": "a", "name": "tag", "pattern": "\\*2ch", "parentNum": HatebuTagParentNum},
    **/
    		// 2ch blogs  
    		//  livedoor
    		{"tag": "div", "name": "entry",
    			"pattern": /http:\/\/blog\.livedoor\.jp\/(insidears|dqnplus)\//},
    		{"tag": "div", "name": "entry",
    			"pattern": /http:\/\/(guideline|alfalfa|news4vip)\.livedoor\.biz\//},
    		//  fc2
    		{"tag": "div", "name": "entry",
    			"pattern": /http:\/\/(imihu|urasoku|news23vip|waranote|vipvipblogblog|netanabe|res2ch|kanasoku|tenkomo)\.blog\d+\.fc2\.com\//},
    		{"tag": "div", "name": "entry",
    			"pattern": /http:\/\/www\.kajisoku\.org\//},
    		
    		// hatena anonymouse diary
    		{"tag": "div", "name": "entry", "pattern": /http:\/\/anond\.hatelabo\.jp\//},
    
    		// tag of "neta"
    		{"tag": "a", "name": "tag", "pattern": "\u30cd\u30bf", "parentNum": HatebuTagParentNum},
    		{"tag": "a", "name": "tag", "pattern": "\\*\u30cd\u30bf", "parentNum": HatebuTagParentNum},
    	];
    
    	for (var i = 0; i < filters.length; i++) {
    		var f = filters[i];
    		filtering(f.tag, f.name, f.pattern, f.parentNum== undefined ? 1 : f.parentNum);
    	}
    
    	function filtering(tag, name, pattern, parentNodeNum){
    		var entrylist = document.getElementsByTagName(tag);
                    //print("pattern = " + pattern);
    		for(var idx = entrylist.length - 1; 0 <= idx; idx--){
    //        for(var idx = 0; idx < entrylist.length - 1; idx++){
    			if (entrylist[idx].className == name){
    				if (entrylist[idx].innerHTML.match(pattern)) {
    					var node = entrylist[idx];
    					var oldNode = null;
    					for (var j = 0; j < parentNodeNum; j++) {
    						oldNode = node;
    						node = node.parentNode;
    					}
    					// print("class = " + oldNode.getAttribute("class"));
    					// print("id = " + oldNode.getAttribute("id"));
    					node.removeChild(oldNode);
    				}
    			}
    		}
    	}
    })();
    

    public class Main {
        public static void main( String[] args ) {
            // ( 5 - 3 ) + 1
            Exp exp = new Add(new Sub(new Num(5), new Num(3)), new Num(1));
            System.out.println(exp.eval());
        }
    }
    
    
    /**
     * 抽象クラス
     */
    abstract class Exp {
        public abstract int eval();
    }
    
    
    /**
     * 足し算
     */
    class Sub extends Exp {
    
        /** 左辺 */
        private Exp hidari;
        /** 右辺 */
        private Exp migi;
    
        /**
         * コンストラクタ
         */
        public Sub(Exp hidari, Exp migi){
            this.hidari = hidari;
            this.migi = migi;
        }
    
        /**
         * 評価
         */
        @Override
        public int eval() {
            return hidari.eval() - migi.eval();
        }
    
    }
    
    
    /**
     * 引き算
     */
    class Add extends Exp {
    
        /** 左辺 */
        private Exp hidari;
        /** 右辺 */
        private Exp migi;
    
        /**
         * コンストラクタ
         */
        public Add(Exp hidari, Exp migi){
            this.hidari = hidari;
            this.migi = migi;
        }
    
        /**
         * 評価
         */
        @Override
        public int eval() {
            return hidari.eval() + migi.eval();
        }
    }
    
    
    /**
     *
     */
    class Num extends Exp {
    
        /**
         *
         */
        private int self;
    
        /**
         * コンストラクタ
         */
        public Num(int self) {
            this.self = self;
        }
    
        /**
         * 評価
         */
        @Override
        public int eval() {
            return self;
        }
    
    }
    

    2008-03-08

    [][greasemonkey][seahorse]はてブのhotentryで、2chコピペブログや「ネタ」を削除す...勝手に改造

    firefoxでしか確認していないけれど、URL正規表現XPathで指定できる様にしてみたよ。

    // ==UserScript==
    // @name           filter for Hatena::Bookmark
    // @namespace      http://anond.hatelabo.jp/
    // @include        http://b.hatena.ne.jp/hotentry*
    // @include        http://b.hatena.ne.jp/entrylist*
    // origin http://anond.hatelabo.jp/20080302214727
    // ==/UserScript==
    (function(){
    	var itemxpath = "//div[@class='entry']";
    	function xpathgenURL(url) {return "//div[@class='entry' and descendant::a[starts-with(@href,'"+url+"')]]"}
    	var filters = [
    		// start with '//' then xpath
    		// moconico douga
    //		{"tag": "div", "name": "entry", "pattern": "nicovideo\.jp"},
    		"//div[@class='entry' and descendant::a[contains(@href,'nicovideo.jp')]]",
    /*
    		// tag of "2ch"
    		{"tag": "a", "name": "tag", "pattern": "2ch", "parentNum": HatebuTagParentNum},
    		{"tag": "a", "name": "tag", "pattern": "\\*2ch", "parentNum": HatebuTagParentNum},
    ***/
    		// start with 'http' then url
    		// 2ch blogs  
    		//  livedoor
    //		{"tag": "div", "name": "entry",
    //			"pattern": /http:\/\/blog\.livedoor\.jp\/(insidears|dqnplus)\//},
    		"http://blog.livedoor.jp/insidears/",
    		"http://blog.livedoor.jp/dqnplus/",
    //		{"tag": "div", "name": "entry",
    //			"pattern": /http:\/\/(guideline|alfalfa|news4vip)\.livedoor\.biz\//},
    		"http://guideline.livedoor.biz/",
    		"http://alfalfa.livedoor.biz/",
    		"http://news4vip.livedoor.biz/",
    		// typeof /regexp/ is function (@firefox) then regexp pattern
    		//  fc2
    //		{"tag": "div", "name": "entry",
    //			"pattern": /http:\/\/(urasoku|news23vip|waranote|vipvipblogblog|netanabe|res2ch|kanasoku|tenkomo)\.blog\d+\.fc2\.com\//},
    		/http:\/\/(urasoku|news23vip|waranote|vipvipblogblog|netanabe|res2ch|kanasoku|tenkomo)\.blog\d+\.fc2\.com\//,
    
    		// tag of "neta"
    //		{"tag": "a", "name": "tag", "pattern": "ネタ", "parentNum": HatebuTagParentNum},
    		"//div[@class='entry' and descendant::a[@class='tag' and string()='ネタ']]",
    //		{"tag": "a", "name": "tag", "pattern": "*ネタ", "parentNum": HatebuTagParentNum},
    		"//div[@class='entry' and descendant::a[@class='tag' and string()='*ネタ']]",
    
    		// hatena anonymouse diary
    //		{"tag": "div", "name": "entry", "pattern": /http:\/\/anond\.hatelabo\.jp\//}
    		"http://anond.hatelabo.jp/",
    	];
    
    	for (var i=0; i<filters.length; i++) {
    		var filter = filters[i];
    		var type = typeof filter;
    		var regexp;
    		var xpath;
    		if (type == "function") {
    			xpath = itemxpath;
    			regexp = filter;
    		} else if (type == "string") {
    			if (filter.match(/^http/)) {
    				xpath = xpathgenURL(filter);
    			} else if (filter.match(/^\/\//)) {
    				xpath = filter;
    			} else {
    				next;
    			}
    		}
    		var removeNodes = document.evaluate(xpath,document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
    		for (var j=0; j<removeNodes.snapshotLength; j++) {
    			var node = removeNodes.snapshotItem(j);
    			if (!regexp || node.innerHTML.match(regexp)) {
    				node.parentNode.removeChild(node);
    			}
    		}
    	}
    })();
    

    ついでに増田版も作ってみたよ。

    // ==UserScript==
    // @name           filter for Hatelabo::AnonymousDiary
    // @namespace      http://anond.hatelabo.jp/
    // @include        http://anond.hatelabo.jp/
    // @include        http://anond.hatelabo.jp/*?page=*
    // @exclude        http://anond.hatelabo.jp/YourID/*
    // ==/UserScript==
    // origin http://anond.hatelabo.jp/20080302214727
    (function(){
    	var itemxpath = "//div[@class='section']";
    	function xpathgenURL(url) {return "//div[@class='section' and descendant::a[starts-with(@href,'"+url+"')]]"}
    	var filters = [
    		// start with '//' then xpath
    		"//div[@class='section' and child::h3[starts-with(string(),'■はてな嫌われ者!')]]",
    		// start with 'http' then url
    		"http://anond.hatelabo.jp/",
    		// typeof /regexp/ is function (@firefox) then regexp pattern
    		/釣り/,
    	];
    
    	for (var i=0; i<filters.length; i++) {
    		var filter = filters[i];
    		var type = typeof filter;
    		var regexp;
    		var xpath;
    		if (type == "function") {
    			xpath = itemxpath;
    			regexp = filter;
    		} else if (type == "string") {
    			if (filter.match(/^http/)) {
    				xpath = xpathgenURL(filter);
    			} else if (filter.match(/^\/\//)) {
    				xpath = filter;
    			} else {
    				next;
    			}
    		}
    		var removeNodes = document.evaluate(xpath,document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
    		for (var j=0; j<removeNodes.snapshotLength; j++) {
    			var node = removeNodes.snapshotItem(j);
    			if (!regexp || node.innerHTML.match(regexp)) {
    				node.parentNode.removeChild(node);
    			}
    		}
    	}
    })();
    

    2008-03-02

    [][]はてブのhotentryで、2chコピペブログや「ネタ」を削除する

    http://anond.hatelabo.jp/20080102122736

    汎用性を上げてみた。

    はてブのhotentryから削除するgreasemonkey

    Sleipnir2のseahorseでも使える。

    // ==UserScript==
    // @name           hatebufilter
    // @namespace      hatebufilter
    // @description    Hatena bookmark filter
    // @include        http://b.hatena.ne.jp/hotentry*
    // @include        http://b.hatena.ne.jp/entrylist*
    // ==/UserScript==
    /*
    問題点
    いまのところなし
    
    ・問題が起こりそうなURL
    http://b.hatena.ne.jp/hotentry?mode=daily&amp;date=20080224
    http://b.hatena.ne.jp/hotentry?mode=daily&amp;date=20080218
    **/
    
    (function(){
    	// Hatebu Tag
    	var HatebuTagParentNum = 3;
    
    	var filters = [
    		// moconico douga
    		{"tag": "div", "name": "entry", "pattern": "nicovideo\.jp"},
    /*
    		// tag of "2ch"
    		{"tag": "a", "name": "tag", "pattern": "2ch", "parentNum": HatebuTagParentNum},
    		{"tag": "a", "name": "tag", "pattern": "\\*2ch", "parentNum": HatebuTagParentNum},
    **/
    		// 2ch blogs  
    		//  livedoor
    		{"tag": "div", "name": "entry",
    			"pattern": /http:\/\/blog\.livedoor\.jp\/(insidears|dqnplus)\//},
    		{"tag": "div", "name": "entry",
    			"pattern": /http:\/\/(guideline|alfalfa|news4vip)\.livedoor\.biz\//},
    		//  fc2
    		{"tag": "div", "name": "entry",
    			"pattern": /http:\/\/(urasoku|news23vip|waranote|vipvipblogblog|netanabe|res2ch|kanasoku|tenkomo)\.blog\d+\.fc2\.com\//},
    
    		// tag of "neta"
    		{"tag": "a", "name": "tag", "pattern": "ネタ", "parentNum": HatebuTagParentNum},
    		{"tag": "a", "name": "tag", "pattern": "*ネタ", "parentNum": HatebuTagParentNum},
    
    		// hatena anonymouse diary
    		{"tag": "div", "name": "entry", "pattern": /http:\/\/anond\.hatelabo\.jp\//}
    	];
    
    	for (var i = 0; i < filters.length; i++) {
    		var f = filters[i];
    		filtering(f.tag, f.name, f.pattern, f.parentNum== undefined ? 1 : f.parentNum);
    	}
    
    	function filtering(tag, name, pattern, parentNodeNum){
    		var entrylist = document.getElementsByTagName(tag);
                    //print("pattern = " + pattern);
    		for(var idx = entrylist.length - 1; 0 <= idx; idx--){
    //        for(var idx = 0; idx < entrylist.length - 1; idx++){
    			if (entrylist[idx].className == name){
    				if (entrylist[idx].innerHTML.match(pattern)) {
    					var node = entrylist[idx];
    					var oldNode = null;
    					for (var j = 0; j < parentNodeNum; j++) {
    						oldNode = node;
    						node = node.parentNode;
    					}
    					// print("class = " + oldNode.getAttribute("class"));
    					// print("id = " + oldNode.getAttribute("id"));
    					node.removeChild(oldNode);
    				}
    			}
    		}
    	}
    })();
    

    hatebufilter.user.jsなどとUTF-8で保存して使う。

    しかし、増田コード記法日本語貼り付けたら化けるんだが・・・どうすればいいんだろ?

    コメントアウトを直せば、2chコピペブログ以外の「2chタグ自体での削除も可能です。

    いろいろ削除していると、まーオレンジニュースでいいじゃんという。

    # スーパー引用記法にしました

    2008-02-23

    はてなダイアリーコメント欄で特定IDコメントあぼーんするgreasemonkey

    ついかっとなって書いた。動くことを優先で書いてるので、変なコードあるかも。

    参考にしたもの:増田にあぼーん機能を追加するgreasemonkey

    // ==UserScript==
    // @name           hatena diary comment filter
    // @namespace      http://anond.hatelabo.jp/
    // @description    abone specified id's comments.
    // @include        http://d.hatena.ne.jp/*
    // ==/UserScript==
    (function(){
       var ignore = [/kyoumoe/, /DASM/];
       var abonemessage = "abone";
        var commentatorIDs = document.evaluate('//a[@class="hatena-id-icon"]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        for (i=0; i < commentatorIDs.snapshotLength; i++) {
            var commentatorID = commentatorIDs.snapshotItem(i);
            var idName = commentatorID.textContent;
            for (j=0; j < ignore.length; j++) {
                var isIgnoreID = ignore[j].test(idName);
                if(isIgnoreID) {
                    break;
                }
            }
            if(isIgnoreID) {
                var commentator = commentatorID.parentNode;
                while(commentator.firstChild) {
                    commentator.removeChild(commentator.firstChild);
                }
                commentator.textContent = abonemessage;
                var commentBody = commentator.nextSibling.nextSibling.nextSibling.nextSibling;
                while(commentBody.firstChild) {
                    commentBody.removeChild(commentBody.firstChild);
                }
                commentBody.textContent = abonemessage;
            }
        }
    })();
    

    2007-12-20

    PukiWikiシンタックスハイライトを入れた

    以下から必要なファイルDLwiki/skinに放り込む。

    http://code.google.com/p/google-code-prettify/

    <?php if (PKWK_ALLOW_JAVASCRIPT) { ?>
      <script type="text/javascript" src="skin/prettify.js"></script>
      <link href="skin/prettify.css" rel="stylesheet" type="text/css"/>
    <?php } //** SETTING FOR GOOGLE CODE PRETTIFY! **//?>
    
    <body onload="prettyPrint()">
    
      Class Pre extends Element
      {
      ...
    
      function toString()
      {
        return $this->wrap(join("\n", $this->elements), 'pre', ' class="prettyprint"');
      }
      }
    

    define('PKWK_ALLOW_JAVASCRIPT', 1);
    

    2007-12-10

    プローチが腐ってるのかもしれないなあ

    なんかデーターが見つからない危険性を感じるというか。ファイルがでかいからreadlinesとか使いたくないんだけれども無理かな。

    class Id_sorted_data
      def initialize path, avarage_bytes_by_one_data, search_margin
        @f = File.open(path);
        @v = avarage_bytes_by_one_data;
        @cash = {};
        @margin = search_margin;
        return self
      end
      def read number
        if @cash.member?(number)
          return @cash[number];
        end
        @f.seek([(number - @margin) * @v, 0].max);
        for i in 1..20
          @f.readline;
          temp = @f.readline;
          @cash[temp.to_i] = temp;
          if temp.to_i == number
            return @cash[number];
          elsif (number - @margin .. number).include?(temp.to_i)
            return near(number);
          end
          @f.seek((number - temp.to_i - @margin) * (@v * (20 - i) / 20), IO::SEEK_CUR);
        end
      end
      def near number
        for i in 1..@margin
          temp = @f.readline;
          @cash[temp.to_i] = temp;
          if temp.to_i == number
            return @cash[number];
          end
        end
      end
    end
    

    2007-12-02

    ここで勝手言語論争ごっこ

    一番使い勝手の良いプログラミング言語Perlどぅわ!

    C/C++/C#なんて気軽に文字列処理できないし、

    Windowsでしか使えないVisualほにゃらら

    メモリの確保、解放なんてしたくない

    だいたい、セグメンテーションフォルトを起こすような言語は嫌い

    DirectXOpenGLとお友達になりたくない

    Haskelわけわかんないし

    Java重苦しいし、いちいちclass Hogehoge { public static void main() { ... } }書くのがめんどくさいし、API多すぎ

    オブジェクト指向したくなるような複雑なプログラムは最初から考えない(作れない)

    GUI作りたくなるような言語はめんどくさい

    言語が提供するGUIのツールはOSとは別に独自のレイヤー世界感を持っててとっつきにくい

    マルチスレッド、排他処理を扱うようなプログラム脳味噌がついて行かないので書かない

    CGIにしか使えないようなPHP

    Ruby、、、そもそもLL言語で大規模でオブジェクト指向プログラム書きたくない。小規模ならオブジェクト指向要らない。

    俺のマシンで実行できないAda/Basic/Fortran/Pascal その他いろいろ

    VHDLVerilogFPGAやゲートアレイなんて持ってない、持ちたくない(苦手だもん)

    データベースなんかいじってたまる

    HTMLXML日本語タグが入り乱れるので、そのつど日本語入力の切替えが死ぬほど嫌になった。

    だから、HTMLXMLは全部手入力なんて真似は絶対してやらねえ。

    Flex(Action Script)はコンパイラJavaで実装されてて重すぎる。(シェルを使えばまし)

    JavaScriptブラウザごとの挙動の違いを吸収しきれる自身が無いので使わない。

    1プログラムにつき、(コメント含めて)250行以上書きたくない

    複雑なプログラムを最後まで書ける連中が羨ましい。

    排他制御にとちって、デスロックしてしまえ。

    (本文には触ったこともない言語を思い込みで罵倒しているなど、嘘、おおげさ、紛らわしいが多数混入しています。それが全部わかった貴方プログラミング言語マスターです。)

    2007-11-22

    http://anond.hatelabo.jp/20070109073629

    ウェブで長文書いて議論してるのを見ると  入れ子Ver.

    いや、本人同士がそれでいいならいいんですけどね、どっちかが長文うぜーなーとか思ってたりする場合も多いわけじゃないですか。ただの野次馬である傍観者にとっては特にそうだと思うんですけど。

    はてブに「長い」とかコメントされてるエントリとかを想起していただけるとありがたいんですけど。

    どうもそういうのを見るとついついフォード・ピント事件なんかを思い出してしまうわけですよ。

    知らない人はググって下さいね。一応映画にもなってたりするんでお暇なら観てみるのもいいんじゃないかとオススメしますですよ、ハイ。(邦題『訴訟』原題『The Class Action』)そんなのめんどくせーよーという人の為には解説してるアドレス貼っときますね。(http://www.fps.chuo-u.ac.jp/~cyberian/Ford_Pinto.html) んでリンク踏むのさえめんどくせーとかacドメインとか読んでもわかんねーよという先入観をお持ちのあなたの為に適当に解説しちゃいますと、フォード日本車に押され始めた時代にこれじゃいかんってわけでコンパクト・カーを売り出したの、ピントっていう。しかしまぁリーズナブルなだけあって結構売れちゃったりしたんだけども安全設計もリーズナブルだったのが問題になって訴訟起こされちゃいました。

    ちなみにアメリカ訴訟制度ってのはなかなか面白いもので、この手の訴訟だとあの有名な懲罰的損害賠償企業が痛いな、と思う範囲まで賠償金額上げちゃっていいよ制度)と、Class Action 制度(同様の被害を受けた人は先人の訴訟結果にタダ乗り出来ちゃう制度、日本だと訴訟提起後結審前までは参加が許されたんだっけ、詳しくは知らないんでゴメンネ)のおかげで絶対負けられないんよね。

    損して得取れなんて無理無理無理無理かたつむり松下幸之助中内功ビックリな制度。

    ここら辺はまぁどうでもいいんで次行きますけど、訴訟開始前にディスカバリーって制度があるんですよ。

    この手の訴訟の場合被告企業側に証拠ってあるもんですしね。それを原告側が請求できる制度なんですよ。

    といっても原告側もそんなに詳しく企業内事情を知ってるわけじゃあないんで請求内容がある程度曖昧にならざるを得ないわけでね、それを逆手に利用して、被告企業がそれこそ倉庫が一杯になる位の量の内部文書を送りつけるって行動に出るわけです。

    そんなことされたら困るよねー。

    原告側は何と言っても少数者であるわけだし、精査しようにも人件費が酷いことになっちゃうし、いかんともし難い状況になったよってお話(この先は映画見てね)。

    ちなみにこのテクニック割とメジャーなようで僕が講義を受けた国際取引法の先生商社出身)もやったことあるって言ってたよ。

    んでこれを最初の話に無理矢理つなげると、長々と文章書いて相手に対応してる人って、誠実に答えてるようでいて実は単に目晦まししてるんじゃないの?って疑問が湧いたよってお話

    2chでよく言われるように3行までしか読めない、ってのは少々極端に過ぎると思うのだけれど、しかしそうそう長い文章を時間コストをかけて読めるわけではないよね、ってところはなんとなく理解してもらえるんじゃないかなぁ。

    世の中には楽しい事やしたい事ってのが一杯あって(セックル恋人とならタダだと思ってても多分風俗の方が安くて気持ちいいよ、次々入れ替わるから新鮮だし)とか食べ歩き(ってよりこんなところで食事してる俺/私って素敵欲望ドライブなんじゃないの)とかもっと寝たい(偽装請負ご苦労様です。所得隠ししなけりゃ設備投資費用も捻出できない世界有数の好景気大企業の下請じゃポイズン)とかデイトレード(まだやってる人いるの?)とか自己啓発はてなFPNってこういう腐臭がするよね、どうでもいいけど。Lifehuckだってw)とか宗教儀式明治天皇の声が聞こえる!!とかグーグルの狂気とか)とかさ)、ネットで議論なんてモノはそのうちの一つでしかないわけだし。

    しかもディスプレイとかブログっていうアーキテクチャはほんと長文読むのに適さない環境なんじゃないかとオールタイプな僕は思ってしまうわけですけども。

    A4一枚分以上は紙で読みたい。

    こういう人って結構いるんじゃないかなぁと思うわけですよ。

    そういう人に対して誠実にあるにはどうしたらいいのかねぇって所はこの発達した世の中でも解決されてないんじゃなかろーか。

    これってビジネスチャンスニートのボクでも起業してお金持ちになってセックル三昧(ここまでjkondo)、金で女は買えると豪語した挙句に証券取引法違反国策捜査の上つかまってあれやこれや的展開が期待できるかもーうはーすげー。

    あ、ごめん妄想入った。

    文章なんてのは長くしようとすればどこまででも長く出来るし、短くしようと思えば結構な割合で短くなるものだと思うんですよ。

    それこそ昨年末K-1みたいな。

    お前2時間でいいじゃんみたいな。

    ただ、短く書く時には削ぎ落とされた部分を読む側が理解してないといけないという条件は付きますが。

    ってことは長々と文章書いてる人ってのは読み手の力量を信用してない、ってことになるんですかね。

    まぁはてブ衆愚とかネットイナゴなんてのが流行ってる昨今さもありなんな発想ではあるわけですが、エントリ書く奴が偉くて読み手は衆愚だからこんなことまでわざわざ書いてやらねばならん、みたいな。

    そんな目で見ないでお願い。

    それならいっそやde√blog終風先生みたいにわからん奴はもういいよ的に放置されるほうがまだマシじゃね? ってか終風先生紫式部か。

    終風日記極東ブログは省略省略で貴族たり得ない衆愚である受験生を困惑させる源氏物語か。

    挙句にシステムスルーカですか、イナゴはイナゴらしくオマニーでも見てればいいですかそうですか。

    切ないなぁ、ああ切ない。

    大島弓子ばりに切ないよ。

    伝われこの思いの8頭身の心境ですよ。

    大体あれだ、平野啓一郎もあれだよな、『日触』しか読んだことないけど漢字難しいよな、初手からお前ら文学イナゴは寄ってくんなオーラ全開だよな。

    昔の日本文学読んでりゃ理解出来るとか言われてもそりゃ無理っすよ、旧字体で読んでる人っているのかね。

    あんなのに造詣が深いのってめっちゃレアモンスターだと思うんですけど。

    なんかはてな内には散見されるのが面白いといえば面白いのかもしれないが。

    しかしまぁあんな小難しい本読むくらいならあれだね、同じ京大出身でも森見登美彦の方が全然面白いと感じちゃうね。

    非モテでわーわー言ってる人は『太陽の塔』でも読むといいよ。

    あ、ここでいう「非モテ」っていうのはある程度の文化資本をもった、顔面や身体に不都合があったり拗れた精神構造持ってる人ね。

    PCなんて気にしねぇ)だってボクの後ろでウメモチオとかessaがグーグルとか権力論とかビジネスモデルとかを語れない期間工みたいな奴ははてなーではない、って囁いて来るんだもん。ここはてなだし仕方ないよね。

    しかしまぁスルーってのも寂しいもんだよね。

    皆が皆スルーし合って出来上がる世界ってアレだよね、なんつーかちょっとつまらんよね。

    ってかこれって物象化じゃないのか。

    マルクスの亡霊は未だ徘徊してるのか。

    ごめんちょっと難しい言葉使ってみたかった。

    中二病といいたければ言うがいいさ。

    でも心の隙間に大阪のおばちゃんを忘れないでいて欲しいなぁと。

    さてもう収拾がつかなくなったのでこの辺で終わりにするけど、長文批判のためにはじめたこのエントリがやたら長くなった事を心より詫びる。

    どうせスルーされるんだろうなぁ。

    2007-11-09

    Pythonではなぜ string.len() でなく len() なのか?

    http://anond.hatelabo.jp/20071021143442

    その理由は知らないが、なければ作ればいいじゃないか。

    class MyString(str):
        def length(self):
            return len(self)
    

    というクラスを作って

    string = MyString("Hello world")
    print string.count("o"), string.length()
    

    Rubyライクにやれば

    2 11

    とでるよ。え、リストもlist.length()が使いたいって?それも簡単。

    class MyList(list):
        def length(self):
            return len(self)
    
    l = MyList([1, 2, 3, 4, 5, 6])
    l.length()
    

    6

    きちんと他のメソッドも使えるよ。

    l[1:]
    

    [2, 3, 4, 5, 6]

    l.reverse()
    l
    

    [6, 5, 4, 3, 2, 1]

    ね。簡単でしょ。

    Pythonは仕組みが統一されているものが多いので、いじりやすいのですよ。上の例のやつは組み込みクラスオブジェクトユーザー定義のクラスオブジェクトがおおむね統一されているからこそ簡単にできる。他にも関数なんかもほかのオブジェクトと同じオブジェクトなので、高階関数なんてもの簡単に作ることができて関数プログラミングぽくできる。例えば今はなきapply関数なんかは

    def myApply(func, *args):
        return func.__call__(*args)
    

    と定義できる。実際に

    def sumUpThree(num1, num2, num3):
        return num1 + num2 + num3
    

    でためしてみる。

    myApply(sumUpThree, 1, 2, 3)
    

    結果はちゃんと

    6

    とでる。将来廃止されそうなmap関数も簡単に定義できる。他にも複数の引数をもつ関数の部分適用のようなことを行う関数も次のように簡単に定義できる。

    def partial(func, *oldArgs):
        def wrapper(*newArgs):
            return func.__call__(*(oldArgs + newArgs))
        return wrapper
    

    sumUpThree関数テストすると

    sum_1 = partial(sumUpThree, 1)
    sum_1(2, 3)
    

    6

    sum_1_5 = partial(sum_1, 5)
    sum_1_5(9)
    

    15

    sum_10_20 = partial(sumUpThree, 10, 20)
    sum_10_20(30)
    

    60

    こういう風に高階関数が簡単にできるのは関数オブジェクト関数の実行とはメソッドの呼び出しにすぎないからだ。以上のように組み込みオブジェクトユーザー定義オブジェクトの差があまりないことや関数オブジェクトであることに見られるようにPythonは仕組みが統一されていてシンプルだ。そのためひとつのことがわかれば他のこともわかることが多いし、簡単にいじることもできる。

    だからなければpythonをいじればいいと思うよ。

    最後にラムダ式信者のためにpartialをラムダ式を使って書いておく。

    def partial(func, *oldArgs):
        return lambda *newArgs:func.__call__(*(oldArgs + newArgs))
    

    Pythonラムダ式がだめだといわれているが、こんな風にかけたとして何がうれしいというのだ。

    ログイン ユーザー登録
    ようこそ ゲスト さん