「def」を含む日記 RSS

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

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-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-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-15

[][]PythonからSWIG経由でVisual Studio 2005を使って困ったこと

以上のような組み合わせで出くわした困ったことと、その解決策をメモしておきます。

setup.py を実行するとエラーが表示された!

Python was built with Visual Studio 2003;

extensions must be built with a compiler than can generate compatible binaries.

Visual Studio 2003 was not found on this system. If you have Cygwin installed,

you can try compiling with MingW32, by passing "-c mingw32" to setup.py.

setup.pyに.iファイルとか.cppファイルを記述して実行すると、こんな感じのエラーメッセージが表示されました。うーん、困った!

http://labs.cybozu.co.jp/blog/mitsunari/2007/08/vc2005boostpython.html

上記のページを参考にして、"%Pythonインストールしたフォルダ%/Lib/distutils/msvcompiler.py"を以下のように修正してみたら解決できました。ありがとうありがとう

--- msvccompiler.py    2007-04-04 17:17:12.000000000 +0900
+++ 
@@ -126,7 +126,7 @@
         self.set_macro("FrameworkDir", net, "installroot")
         try:
             if version > 7.0:
-                self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
+                self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv2.0")
             else:
                 self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
         except KeyError, exc: #
@@ -252,7 +252,10 @@

     def initialize(self):
         self.__paths = []
-        if os.environ.has_key("DISTUTILS_USE_SDK") and os.environ.has_key("MSSdk") and self.find_exe("cl.exe"):
+        if self.__version >= 7.1 or (
+            os.environ.has_key("DISTUTILS_USE_SDK") and
+            os.environ.has_key("MSSdk") and
+            self.find_exe("cl.exe")):
             # Assume that the SDK set up everything alright; don't try to be
             # smarter
             self.cc = "cl.exe"
@@ -288,10 +291,16 @@

         self.preprocess_options = None
         if self.__arch == "Intel":
-            self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' ,
-                                     '/DNDEBUG']
-            self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
-                                          '/Z7', '/D_DEBUG']
+            if self.__version >= 7.1:
+                self.compile_options = [
+                    '/nologo', '/Ox', '/MD',  '/W3', '/EHsc', '/DNDEBUG']
+                self.compile_options_debug = [
+                    '/nologo', '/Od', '/MDd', '/W3', '/EHsc', '/Z7', '/D_DEBUG']
+            else:
+                self.compile_options = [
+                    '/nologo', '/Ox', '/MD',  '/W3', '/GX',  '/DNDEBUG']
+                self.compile_options_debug = [
+                    '/nologo', '/Od', '/MDd', '/W3', '/GX', '/Z7', '/D_DEBUG']
         else:
             # Win64

cl.exeが見つからないと言われた!

setup.pyを実行するとcl.exeが見つからないみたいなエラーが表示されました。これは、アレだ。「パス通せ!」ということですね。bashを起動するときのバッチファイル(たぶん"cygwin.bat"とか)で、以下のような行を入れてやれば解決しました。

call "%VS80COMNTOOLS%vsvars32.bat"

setup.pyを実行したときに"basetsd.h"が開けないと言われた!

d:\python25\include\pyconfig.h(189) : fatal error C1083: include ファイルを開けません。'basetsd.h': No such file or directory

error: command 'cl.exe' failed with exit status 2

setup.pyを実行すると、上のようなエラーが表示されました。

http://d.hatena.ne.jp/ousttrue/20070531/1180556273

上記のサイトを見るとインクルードパスが通っていない場所に"basetsd.h"があるのが原因なので、"cygwin.bat"にインクルードパスの設定をしておきました。

call "%VS80COMNTOOLS%vsvars32.bat"
set INCLUDE=C:\Program Files\Microsoft Platform SDK\Include;%INCLUDE%

setup.pyを実行したときのリンク時にエラーが発生した!

link: extra operand `/INCREMENTAL:NO'

詳しくは `link --help' を実行して下さい.

error: command 'link.exe' failed with exit status 1

これは、cygwinのほうのlink.exeが実行されてるのが原因でした。スマートな解決策ではありませんが、cygwinのほうのlink.exeをリネームして解決。パスの設定順序とかでどうにかできるといいんだけど、どうすればいいんかな。

MSVCR80.dllが見つからないと言われた!

MSVCR80.dllが見つからなかったため、このアプリケーションを開始できませんでした。アプリケーションインストールし直すとこの問題は解決される場合があります。

SWIGが生成した.pyファイルをimportしたら、こんな感じのエラーダイアログが表示されたよ。うーん、困った!

http://d.hatena.ne.jp/moriyoshi/20070525

上記のページを参考にして、"%Pythonインストールしたフォルダ%/python.exe.manifest"として以下のようなファイルを新しく作ったら、解決できました。ありがとうありがとう

あとこれ、bashから実行したらエラーダイアログが表示されず、importするモジュールが見つからないみたいなエラーメッセージが出力されるだけだったよ。

<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' />
    </dependentAssembly>
  </dependency>
</assembly>

2008-02-09

なんとなく目についたから

すっごい前の記事だけど。

http://anond.hatelabo.jp/20071110221822:title

はじめはRubyで書こうと思ったけど挫折した。だれかRubyで書いてくれないかな・・・。

ruby だとこんな感じじゃないでしょうか。

# 問3だけ。pythonコードコピペ

関数オブジェクトなのに引数にできないの?なんで?

ruby には関数はないです(多分)。全部メソッド。で、関数に見えるのはトップレベルにある Kernel オブジェクトのメソッド。メソッドを変数にして実行したいなら、method オブジェクトを作るか、send メソッドを使ってメッセージパッシングすればいい。でも普通 proc を使うと思う。

def foldr(callee, lst, obj)
    if lst.empty?
        obj
    else
        callee.call(lst[0], foldr(callee, lst[1..-1], obj))
    end
end

def apply(callee, obj)
    callee.call(obj)
end

def plus5(num)
    num + 5
end

def times10(num)
    num * 10
end

def divide2(num)
    num / 2
end

foldr(method(:apply), [:plus5, :times10, :divide2].map{|m| method(m)}.reverse, 10)
foldr(method(:apply), ([:plus5, :times10].map{|m| method(m)} + [lambda {|num| num / 2}]).reverse, 10)

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-11-10

関数プログラミングにおけるFizzBuzz問題

いくつか考えてみた

(問1)高階関数再帰関数を必ず使って数値を要素とするリストの要素の総和を求める関数を書け。ただし高階関数を使うという要件と再帰関数を使うという要件は同じ関数で満たしてもよい。

(問2)二つの引数をとり二つのうち大きいほうを返す関数高階関数再帰関数をつかって数値のリストの最大値を求める関数を書け。ただし高階関数を使うという要件と再帰関数を使うという要件は同じ関数で満たしてもよい。

というのは簡単すぎるか?簡単すぎるなら

(問3)高階関数再帰関数を必ず使ってある数値に5を足し、10かけて2で割った数を求める関数を書け。ただし高階関数を使うという要件と再帰関数を使うという要件は同じ関数で満たしてもよい。

こっちの方がいいかな。でもトリッキーすぎる気もする。

一応問題を出したので、SchemePythonで自分で想定している答えを書いておいた。はてなではSchemeが人気のようなので、あまり知らなかったけど関数言語ではSchemeで書いておいた。Pythonで書くのはSchemeだけだとわかりにくいので、なにかスクリプト言語で書いておこうと思ったから。Rubyの方が人気なので、はじめはRubyで書こうと思ったけど挫折した。だれかRubyで書いてくれないかな・・・。コードオブジェクトってなによ。というか関数オブジェクトなのに引数にできないの?なんで?(以下疑問と愚痴の嵐なので略)Perlは古株が多くてユーザー数も多そうだけど、・・・その・・・無理です・・・。あの言語仕様はやる気がしない。ぶっちゃけ理解できない。Pythonを知らないひとは多そうだけど、知らなくてもSchemeよりは感じは掴めると思うのでPythonでも書いておくことにした。

Scheme

http://anond.hatelabo.jp/20071110215936

Python

http://anond.hatelabo.jp/20071110220132

これで大部分のひとがこの問題に興味をもたなくて解答するひとがいなくても、興味を持ったひとは安心だね!

追記:

問3で次のは無しとしておきたい。

Scheme

(define continuous-apply
    (lambda (lst obj)
        (cond
            ((null? lst)
                obj)
            (else
                (continuous-apply (cdr lst) ((car lst) obj))))))

(define plus5
    (lambda (num)
        (+ num 5)))

(define times10
    (lambda (num)
        (* num 10)))

(define divide2
    (lambda (num)
        (/ num 2)))

(define plus5-times10-divide2
    (lambda (num)
        (continuous-apply (list plus5 times10 divide2) num)))

(plus5-times10-divide2 2)

Python

def continuousApply(lst, obj):
    if lst:
        return continuousApply(lst[1:], lst[0](obj))
    else:
        return obj

def plus5(num):
    return num + 5

def times10(num):
    return num * 10

def divide2(num):
    return num / 2

def plus5_times10_divide2(num):
    return continuousApply([plus5, times10, divide2], 2)

plus5_times10_divide2(2)

pythonによる解答

Rubyと違って空のリスト配列?)は偽。

(問1)


def foldl(func, obj, lst):
    if lst:
        return foldl(func, func(obj, lst[0]), lst[1:])
    else:
        return obj

def plus(num1, num2):
    return num1 + num2

def mySum(lst):
    return foldl(plus, lst[0], lst[1:])

・例

mySum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])


(問2)

def foldl(func, obj, lst):
    if lst:
        return foldl(func, func(obj, lst[0]), lst[1:])
    else:
        return obj

def large(num1, num2):
    if num1 >= num2:
        return num1
    else:
        return num2

def myMax(lst):
    return foldl(large, lst[0], lst[1:])

・例

myMax([34, 55, 77, 1, 2, 3, 4, 100, 98])

(問3)

def foldr(func, lst, obj):
    if lst:
        return func(lst[0], foldr(func, lst[1:], obj))
    else:
        return obj

def myApply(func, obj):
    return func(obj)

def plus5(num):
    return num + 5

def times10(num):
    return num * 10

def divide2(num):
    return num / 2

def plus5_times10_divide2(num):
    funcList = [plus5, times10, divide2]
    funcList.reverse()
    return foldr(myApply, funcList, num)

・例

plus5_times10_divide2(5)


(参考)

def makeProceduce(lst):
    lst.reverse()
    return lambda num: foldr(myApply, lst, num)

・例

divide2_plus5_times10 = makeProceduce([divide2, plus5, times10])
divide2_plus5_times10(4)

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ラムダ式がだめだといわれているが、こんな風にかけたとして何がうれしいというのだ。

2007-10-14

速学Ruby railsを使うための最小限の知識

前置き

本講座ではほかの言語は触ったことがあるが、Ruby未経験者を対象にRailsを触るための最小限のrubyの知識を身につけるためのものである。

と、堅苦しく書いたけど仕事で急いでrailsを触れうようになるための最低限の情報だけをまとめてみたよ。

これを読んだだけではRubyプログラムを書けるようにはならないと思うけど、Railsを使った開発だといきなり何もできないってことはなくなるはずだよ。

肩の力を抜いて読んでほしいな。

基本のお話

変数宣言

Rubyでは特別に変数の宣言は必要ないんだ。

いきなり今まで宣言していない名前を使ってもおこらえないよ。

answer_to_life_the_universe_and_everything = 42 
# answer_to_life_the_universe_and_everything ????~A~S??~S????~H~]??~A????~Y??| ??~A~Y??~K??~B

なんて書いても問題ないんだ。

静的言語をやってきた人には型の宣言がないことに違和感を感じる人もいるかもしれないけど、Rubyでは型はあんまり気にしないんだ。

int x = HogeHoge.new

みたいな書き方では

変数xを用意してそこにHogeHoge.newを代入する。といった説明を受けたと思う。

でもRubyではちょっと違う考え方をするんだ。

x = Foo.new

だとFoo.new変数を用意して、それを名前に"束縛"するって説明することが多いよ。

ベルが貼られた箱を用意してそこに何かを入れるか、何かが入った箱を用意してそれにラベルを張るかの違いだね。後者のほうがちょっとだけ柔軟なんだ。

そして、柔らかいほうがRuby流って気がするよ。

でもそんなRubyでもちょっとだけ名前に関するルールがある。

大文字からはじまる名前はクラス

小文字からはじまる名前が局所変数

@からはじまるのがインスタンス変数

@@からはじまるのがクラス変数

ってルールだよ。

Railsだったらとりあえず@からから始まる名前にしておけばたいていは問題ないんじゃないのかな。

分岐分岐

JavaC言語ではif文なんかをよく使うよね。Rubyでもほとんど一緒だよ。

 if something_condition
  do_some_thing
elsif other_condition
  do_another_thing
else
  do_something_if_you_wont
end

もちろんelsifやelseが必要なければ省略することができるよ。

ほかにもRubyには便利な分岐の書き方があるよ。

so_some_thing if some_condition

みたいに後置でも書けるんだ。

どっちでも好きなほうを使えばいいと思うよ

関数宣言

関数宣言のとてもシンプルなんだ。

def func_name ( some_val_names )
  do_some_thing
end

といった感じでdef..endでくくるだけで簡単に関数を宣言できるよ

そして関数戻り値は最後に評価した値が自動的に戻り値になるんだ。

def baz 
  10
end

これは常に10を返す関数の例だよ。

Hash,Array

プログラムをしていて非常によく使うデータの型といえば配列(Array)や連想配列(Hash)だよね。

Rubyではそれらを簡潔に使うために専用の文法を用意している。

それが[]と{}だ。

arr = [1,2,3,"A","B","CDE"]         #??~M很~W宣訾@
has = {1 => "A",2 => "B", 3 => "C"} #??~O??~C????~CťΣ訾@
puts arr[3]
puts has[2]
A
B

とすれば簡単に配列ハッシュが宣言できるよ。

アクセス方法もほかの言語と大きく変わらないのが分かるよね。

pythonクラスを使ったけど

class BTree:
    def __init__(self, node_info=None):
        self.root = BNode(node_info)
        self.left = None
        self.right = None

    @classmethod
    def printNode(self, n):
        print "%s " % n.info,

    def walk(self, node = self.root, Execute=BTree.printNode):
        ... ...

walkメソッドは引数エラーがでる。selfがわかんないみたい。name 'self' is not defined

そりゃそうか、実行時にselfが決まるんだから。でもname 'BTree' is not definedになるのは何故?

2007-09-25

プログラム学習

http://anond.hatelabo.jp/20070924220240

最初の頃は単なる知識を得る作業だから仕方ない。つまらないが、最初に知識をかき集めるその方法が一番効率がいいから、テキストはそういう構成になっている。面白いのは、手元に材料が一通り揃ってから、「この課題を解決せよ、手段は問わない」という類の問題が出される段階になった時。

2chプログラミング板の宿題スレに一時期住んでたんだけど、一番面白いと思った課題は「迷路を解くプログラムを作れ。実装方法はいっさい問わない。速いプログラムほど良いとする」というもの。「迷路は壁=1、通路=0みたいなテキストデータで与えられ、入り口と出口は壁に隣接…」みたいな仕様だけは与えられたが、それ以外何もなし。

解決方法を考えるのは楽しいし、自分にできない事をやってくれるプログラムというのはコンピュータが自分で考えているみたいで妙に愛着が湧く。

プログラムにやらせたい作業が見つからない、というのは多分想像力の欠如でもなんでもなく誰もが通る道。実際、日常生活でそんな機会はなかなか無い。

自分の場合、Perlwebサイト関連ツールを作るところから始めたかな。フォームに入力した内容が追加されていくだけの簡単な日記帳とか、後はカウンターとか、そんなものをチマチマ作る事から始めた。ほとんどはもう使ってないが、タブ区切りテキストデータhtmlのtableに直すスクリプトは地味に今でもしょっちゅう使う。

2chdatを読み込ませて書き込み数の遷移を出すプログラムhttp://www.tv2ch.info/?3 みたいなの)も、時間辺りの書き込み数データテキストで書き出すところまで作ったな(Excelコピペしてグラフツールを使えばこのサイトのようなグラフが書ける所まで)。テキスト周りの処理が出来るならば簡単に書ける。

あとは、Ragnarok Onlineで時間辺りダメージを最大にするためにはどのようにステータスを成長させれば良いかの算出をプログラムにやらせたりもした。あり得るステ振り全てに対し総当り探索。(分かる人だけに注釈を入れると職業ハンターで、算出値は鷹ダメージ込み。敵のdefに応じてダメージが変わってくるんで複数パターン出した)

ネトゲだと、某ゲームでアイテムの相場入力すると、儲かりそうな生産レシピを出力してくれるプログラムも書いた。書いたが、打ち込むべきデータを集めるのが面倒すぎて使用は断念した。

総当りで思い出したが、HTMLで色見本を書き出すプログラムも書いた。http://www.tohoho-web.com/wwwcolor.htm の216色見本みたいなのをプログラムで生成させた。

教科書でやった課題は、ソートプログラムくらいかな。アルゴリズムの流れだけ理解したら、教科書を閉じてそのアルゴリズムを自力で実装する。写経は面倒でやれなかった。

2007-07-11

めいろさくせーぷろぐらむつくってみたー

http://anond.hatelabo.jp/20070711013155

人待ちの間暇だったから作ってみたよー。

さっくり作ったから無駄が多い気がするけど、とりあえず動いた。

全体が埋まったかどうかの判定をしないでひたすらまわしまくってるし、なんか綺麗な形じゃないけど、とりあえず投稿しちゃうよ!

これ綺麗な形にするの結構難しいんじゃないかな。面白いね。

class Maze
	class Box
		WALL = 0
		LOAD = 1
		def initialize(state = LOAD)
			@state = state
		end
		def is_wall?
			return true if @state == WALL
			false
		end
		def is_load?
			return true if @state == LOAD
			false
		end
		def set_wall
			@state = WALL
		end
		def set_load
			@state = LOAD
		end
	end
	TOP = 10
	RIGHT = 11
	BOTTOM = 12
	LEFT = 13
	def initialize(height = 100, width = 60)
		@height = height
		@width  = width
		@data = Array.new
		self.fillbox
		@counter = 1000
		@root = Array.new
	end
	def choose_random
		return rand(@width-3)+1, rand(@height-3)+1
	end
	def choose_direct
		t = rand(4)+10
		return t
	end
	def move(x, y, t)
		new_x = x
		new_y = y
		case t
		when TOP
			new_y -= 1
		when RIGHT
			new_x += 1
		when BOTTOM
			new_y += 1
		when LEFT
			new_x -= 1
		end
		return new_x, new_y
	end
	def make_load?(x, y)
		return false if x < 0 or y < 0 or x > @width-1 or y > @height-1
		return false if @data[x][y].is_load?
		true
	end
	def dead_end?(x, y, t)
		case t
		when TOP
			if make_load?(x-1, y) and \
				 make_load?(x-1, y-1) and\
				 make_load?(x, y-1) and\
				 make_load?(x+1, y-1) and\
				 make_load?(x+1, y) then
				 return false
			end
		when RIGHT
			if make_load?(x, y-1) and \
				 make_load?(x+1, y-1) and\
				 make_load?(x+1, y) and\
				 make_load?(x+1, y+1) and\
				 make_load?(x, y+1) then
				 return false
			end
		when BOTTOM
			if make_load?(x-1, y) and \
				 make_load?(x-1, y+1) and\
				 make_load?(x, y+1) and\
				 make_load?(x+1, y+1) and\
				 make_load?(x+1, y) then
				 return false
			end
		when LEFT
			if make_load?(x, y+1) and \
				 make_load?(x-1, y+1) and\
				 make_load?(x-1, y) and\
				 make_load?(x-1, y-1) and\
				 make_load?(x, y-1) then
				 return false
			end
		end
		true
	end
	def check_all?(stack)
		for i in TOP..LEFT do
			return false if !stack.include?(i)
		end
		true
	end
	def extend(x, y)
		@data[x][y].set_load
		check_stack = Array.new
		loop do
			t = choose_direct
			check_stack << t
			new_x, new_y = move(x, y, t)
			if enable_new_point?(new_x, new_y)
				if !dead_end?(new_x, new_y, t) then
					@root << ["ex", new_x, new_y, t]
					extend(new_x, new_y)
					break
				end
			end
			break if check_all?(check_stack)
		end
	end
	def enable_new_point?(x, y)
		return false if x<=0 or y <= 0 or x > @width-1 or y > @height-1
		return false if @data[x][y].is_load?
		true
	end
	def _make
		x = y = 0
		loop do
			x, y = choose_random
			break if @data[x][y].is_load?
		end
		@root << [x,y]
		extend(x, y)
	end
	def make_goal
		x = @width - 2
		y = @height - 1
		for i in 1..@width-3
			if @data[x-i][y-1].is_load? then
				@data[x-i][y].set_load
				break
			end
		end
	end
	def make_start
		for i in 1..@width-3
			if @data[i][1].is_load? then
				@data[i][0].set_load
				break
			end
		end
	end
	def make
		self.fill_all
		x, y = choose_random
		@root << [x,y]
		@data[x][y].set_load
		extend(x,y)
		while(!is_fill?) do
			_make
		end
		make_goal
		make_start
		self
	end
	def is_fill?
		@counter -= 1
		return true if @counter < 0
		false
	end
	def fillbox
		for i in 0...@width do
			@data[i] = Array.new
			for j in 0...@height do
				@data[i][j] = Box.new
			end
		end
	end
	def fill_all
		for i in 0...@width do
			for j in 0...@height do
				@data[i][j].set_wall
			end
		end
	end
	def output
		for i in 0...@height do
			for j in 0...@width do
				if @data[j][i].is_wall? then
					print "#" 
				else
					print " "
				end
			end
			print "\n"
		end
	end
end

Maze.new(30, 30).make.output

## output

# ############################
#  ##    # #     ##    #     #
##    ##   ## ## #  ##   ### #
### #  ###  #  #   #### ###  #
#   # ## ## ## ### #  #  #  ##
### #  #  #  #   #### ## # ###
##  ## ## ## ###   #  ## #   #
#  ### #  #   #### # ##  ## ##
# ## #   ####    #    # ###  #
#  # ###  # #### # ## # #   ##
##     ## #      #  ### ######
######  ### #######   ###    #
#    ##   ###   # # #   ## # #
# #######  #  # # # ###  ### #
#   # # ##   ##   #   ##   # #
### #    ######## ########   #
#   # ## ##     #    #     ###
# ### ##  ## ##   ##   ##### #
#      ##  # #  #  ###  #    #
# ######## # #####   ## # # ##
#       ## #  #  #####  # #  #
####### #  ## # ## #   ## ## #
##    ### ###   ##   ####### #
#  ##  ##   ## ##  ###    ## #
# ####  ###  # #  ## # ##    #
#  # ##   ## # # ###    ######
## #  # #  # # #   ## #    ###
#    ## ## #   # #  # # ##  ##
# # ##  ##   # # ##   #  ##  #
########################### ##

2007-07-01

[] 増田Readerのデザイン(案)

左ペインには再読込するたびにに更新される目次(カテゴリーに基づく、括弧内は記事数)
↓         カテゴリーソートされた記事が表示される右ペイン
abc(640)| ■Title
bcd(512)| 
cde(256)|
def(128)|
efg(064)|                   | トラックバック(0) | **:**
fgh(032)|
    | ■Title
    |
    |
    |                   | トラックバック(0) | **:**

2007-06-12

増田Readerデザインイメージ

左ペインには再読込するたびにに更新される目次(カテゴリーに基づく、括弧内は記事数)

↓         カテゴリーソートされた記事が表示される右ペイン

abc(640)| ■Title

bcd(512)| 

cde(256)|

def(128)|

efg(064)|                   | トラックバック(0) | **:**

fgh(032)|

     | ■Title

     |

     |

     |                   | トラックバック(0) | **:**

2007-05-11

http://anond.hatelabo.jp/20070511021557

剰余不可より100回ループ不可、てほうが制限としては面白いかもね。

まあそれは再帰するだけの話で

$arr = [nil,"Fizz","Buzz","FizzBuzz"]
def fzbz(i)
	f = ((i%3) == 0) ? 1 : 0
	f |= ((i%5) == 0) ? 2 : 0
	$arr[0]=i
	puts $arr[f]
	fzbz(i+1) if i<100
end

fzbz(1)

これを変形すると、条件判断&ループ禁止が書けるな。

$arr = [nil,"Fizz","Buzz","FizzBuzz"]
$f3 = [1,0,0]
$f5 = [2,0,0,0,0]
def fzbz(i)
	f = $f3[i%3]
	f |= $f5[i%5]
	$arr[0]=i
	puts $arr[f]
	([lambda{|i| fzbz(i+1)}, lambda{}])[i/100][i]
end

fzbz(1)

C言語でも関数ポインタで書けるので、Rubyの特権ではない。

2007-05-10

偽FizzBuzz問題

http://anond.hatelabo.jp/20070508170219

「%(剰余演算子)使用禁止」みたいな話があったので、逆に無駄馬鹿っぽいプログラムが出てくると、どれくらい間抜けな「剰余を求める関数」になるか見てみたい。

……いざ間抜けに作ろうとすると、それはそれで難しい。

#!ruby -Ks

# 3で割り切れるなら0を返す
# 割り切れないときは各桁の数を足した数字が返ってくるけど、再帰用だから気にしないでね><
def mod3_equal_zero(i)
  s = i.to_s
  num = 0
  s.split('').each {|c|
    num += c.to_i
  }
  if (num > 9) then
    num = mod3_equal_zero(num)
  end
  num=0 if num == 3 || num == 6 || num == 9
  num
end

# 5で割り切れるなら0を返す
def mod5_equal_zero(i)
  s =i.to_s
  i=0 if s[-1,1] == "0"
  i=0 if s[-1,1] == "5"
  i
end

# メイン
(1..100).each{|i|
  str =""
  str += "Fizz" if mod3_equal_zero(i) == 0
  str += "Buzz" if mod5_equal_zero(i) == 0
  str = i.to_s if (str == "") 
  print str,"\n"
}

2007-05-08

みんなアタマやわいね

想定していた回答はこんなの

def setr(a,k,str)
	a[k-1] = str if a[k-1]==nil && k<=100
end

res = Array.new(100)
(1..100).each{|i|
	setr(res,i   ,i.to_s)
	setr(res,i*3 ,"Fizz")
	setr(res,i*5 ,"Buzz")
	setr(res,i*15,"FizzBuzz")
}

print res.join("\n")

http://anond.hatelabo.jp/20070508173455 が気に入ったのでこなたあげます っ(=ω=.)

2007-02-08

一方Rubyは全てをオブジェクトにすることを選んだ

テンプレートなんて概念すらない

def add(x,y)

 x+y

end

p add(1,2) # => 3

p add("1","2") # => "12"

http://anond.hatelabo.jp/20070208115307

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