2014年2月21日 星期五

陣列洗牌程式(shuffle array)

陣列如何把它的順序打亂,作出類似洗牌的效果,我一直都很頭痛,搞得非常的複雜。至從用了Ruby,Array物件包含shuffle方法之後,我就沒思考過陣列洗牌的問題了,反正Ruby幫我處理得好好的。

Ruby

# 52張牌的牌堆
poker = (1..52).to_a    # => [1, 2, 3, ... , 52] 
# 洗牌打亂
shuffled = poker.shuffle           # => [22, 32, 12, ...
# 也可以直接打亂原來的陣列
poker.shuffle!          # => [39, 47, 3, ...

Javascript

Javascript我就頭痛了,我得自己寫洗牌的方法。我在網路上找到了這個演算法,仔細看了之後,才知道原來洗牌可以這麼簡單:

// 原本for迴圈是一行程式,太難理解,這邊改寫成多行
function shuffle(o){
    for(var j, x, i = o.length; i;){
        j = Math.floor(Math.random() * i);

        // javascript的array是0-base
        // 所以迴圈第一次進入,--i後表示陣列最後一個位置。
        x = o[--i];
        o[i] = o[j]; 
        o[j] = x;
        // 以上三行代表以x為temp, o[i], o[j]做交換
    } 
    return o; //回傳陣列,我一開始也看錯看成回傳0
};

變數說明

  • 引數o: 將被洗牌的陣列
  • for迴圈內
    • i : 將會從陣列的最後一個位置,慢慢往前移到第一個位置(但移到第一個位置時for迴圈不執行,因為Javascript的數值0也代表false,會離開迴圈。0代表flase這點跟Ruby不一樣)
    • j : 將會被亂數選擇,選到要被交換的位置
    • x : 用來暫存o[i]的數值,幫助o[i]與o[j]做數值交換

就這樣從最後一個位置開始,依次往前隨機挑選一個位置與它交換(可能挑到自己,表示不交換),來達到洗牌的效果,陣列多大,就執行幾次,時間複雜度O(n),效率也不錯。 注意這邊傳入的陣列是call by reference,會修改原來呼叫方法時傳入的陣列,所以直接:

// 5張牌的牌堆
poker = [1, 2, 3, 4, 5];
shuffle(poker);
// poker 就直接被打亂了

如果不想打亂原來的牌堆,就稍微改寫一下,不要動到原來的陣列:

function shuffle(o){ //v1.0
    var ary = o.slice(0);
    for(var j, x, i = ary.length; i;){
        j = Math.floor(Math.random() * i);
        x = ary[--i], ary[i] = ary[j];
        ary[j] = x;
    }
    return ary;
};

這邊用到了一個小技巧slice(start, end),這是javascript用來複製陣列的一個區塊,指令開始與結束的位置,小技巧是如果傳入0,就直接複製整個陣列。如果你想說:

啊直接ary = o 不是更快

呵呵,我建議你從C的指標在開始複習一下。

C語言

使用C語言,改些小地方,C語言的rand()產生的是0 ~ RAND_MAX的一個數字,所以直接j = rand() % i;就行了。還有C語言的0也表示false的意思,大概只有Ruby跟大家不同,目前我只見過Ruby的0表示true。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int *shuffle(int *o, int length){
    int i,j,x;
    for(j, x, i=length; i;){
        j = rand() % i;
        x = o[--i];
        o[i] = o[j];
        o[j] = x;
    }
    return o;
}

int main(void) {
    srand(time(NULL));
    int i, poker[] = {1, 2, 3, 4, 5};
    shuffle(poker, 5);
    for(i=0; i&lt;5; i++)
        printf("%d ", poker[i]);
    return 0;
}

這邊我是用call by address的寫法,如果你不想改變原來的陣列。C語言有一堆記憶體的函式讓你用,你可以宣告一塊記憶體空間(malloc),然後記憶體複製(memcpy),之後傳入shuffle去洗牌

不建議在shuffle函式裡面宣告記憶體區塊,因為什麼時候要free掉記憶體很麻煩,交給呼叫他的函式去決定什麼時候free掉比較正確,shuffle函式就專心洗牌就好。

JQuery

在網頁上面,要把一些HTML的元件打亂,可以直接在JQuery裡面加個物件方法給他:

jQuery.fn.shuffleElements = function () {
    var o = $(this);
    // 這邊的for迴圈,使用原本的一行寫法
    for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
    return o;
};

裡面那個for迴圈洗牌的演算法沒變。

至於一些基本的jQuery用法,jQuery.fn,可以直接取得jQuery的prototype,關於javascript的prototype是什麼,請至函式 prototype 特性觀看。這邊使用jQuery.fn就可以直接在jQuery的protoype裡面加入shuffleElements方法,然後就直接傳入一堆HTML元件讓他去洗牌,譬如你的網頁有一堆圖片,簡單一點就這樣:

var img = $("img").shuffleElements();
$("body").append(img);

這麼寫的話,可以簡單地把你網頁裡面的圖片全部洗牌之後,再全部放回<body>最下面。這邊主要是講洗牌,你可以改一改把它變成<ul>的順序之類的,或其他的應用。

2014年2月19日 星期三

Rails 在CSS裡使用圖片

暴力法

反正最後都會被編譯在public下面,可以下rake assets:precompile指令,看看最後圖片被放到哪裡去了,通常都在public/asset底下,而網頁伺服器的根目錄是public/所以你的css就直接把圖片定位在/assets/底下,例如:

.your_class {
    background-image:url('/assets/your_img.png');
}

非常不建議用這個方法,程式碼非常不靈活

內嵌Ruby法

在Rails裡面,你的檔案想要內嵌Ruby,只要把檔名結尾加上.erb,就可以最常見的就是page.html.erb,但可沒這麼簡單唷,就連CSS檔以可以內嵌Ruby,一樣把你的CSS加上.erb,譬如style.css.erb,這樣CSS內就可以使用Ruby了。

既然可以使用Ruby了,那就大膽的把Rails內建的Helper拿出來用吧,asset_path("img.jpg"),就行了,例如:

.your_class {
    background-image:url('<%= asset_path("your_img.png") %>');
}

這樣至少靈活多了,至少你不用擔心Rails編譯之後,會把你的圖檔放到Public的哪裡去,被改了什麼檔名。

SCSS法

Rails預設使用SCSS,你打開你的Gemfile就可以看到了,預設已經有gem 'sass-rails',在Rails的CSS裡面取圖,我推薦這個方法,因為這個方法程式碼最簡潔:

.your_class {
    background-image: image-url("your_img.jpg");
}

發生什麼事?沒錯使用SCSS的話,image-url("your_img.jpg");就行了,image-url是Rails的Helper,可別跟CSS原生的background-image:url(your_img.jpg);給搞混囉

.your_class {
    /* 這個是Rails的Helper */
    background-image: image-url("your_img.jpg"); 

    /* 這個是CSS的原生寫法 */
    background-image: url(your_img.jpg);
}

這兩種寫法要到的圖片可不一樣唷,Rails的Helper編譯後,會自動幫你安排圖片的路徑。而CSS原生的寫法,你就得自己編譯過後,自己去public/asset裡面找,你的圖跑到哪去了。

2014年2月14日 星期五

Ruby,檔案定期備份

我最近把資料庫系統換成了SQLite,直接放到RamDisk去執行。今天想要寫支程式,可以定時檢查RamDisk,裡面的sqlite檔有沒有變動,有變動就備份到硬碟去。於是使用Ruby寫下了:

FILE_TO_CHECK = ARGV[0]
FILE_TO_SAVE = ARGV[1]

if !File.exists?(FILE_TO_SAVE) 
  `cp #{FILE_TO_CHECK} #{FILE_TO_SAVE}` 
  exit
end

check_file = File.new FILE_TO_CHECK 
save_file = File.new FILE_TO_SAVE

if check_file.mtime > save_file.mtime
  `cp #{FILE_TO_CHECK} #{FILE_TO_SAVE}` 
end

不錯,不錯,想說這樣這隻程式可以放到crontab去,將來有同樣要檢查的檔案就編輯在crontab裡:

0 * * * * ruby /check_file.rb sinkfile sourcefile

不過做到這邊,突然想到,安裝rsync不就行了,直接

0 * * * * rsync sinkfile sourcefile

rsync就是檢查檔案有無更動,有更動的才會複製,而且它是這麼老牌又穩定的lib了,呵呵,沒關係,就當做是在練習使用vim編輯器

2014年2月13日 星期四

Ruby,Time.new(自定時間格式)

Ruby 製作Time物件時,必須遵守一定的格式,譬如

Time.new(2014, 2, 13, 16, 30, 30, "+08:00") #=> 2014-02-13 16:30:30 +0800

Time.new裡頭接受的參數從年,月,日,時,分,秒,時區,沒填的就預設是0,全部都沒填,那就給系統當前時間。那問題如果我們得到的時間是一個字串呢?例如我們有一個時間字串:

2014-02-13 16:50:47 +0800

這串文字當參數傳到到Time.new裡面只會出現錯誤訊息,難道要開始做苦工先字串解析,在放到Time.new裡面嗎?

Time.strptime

用到字串解析也就太辛苦了,Ruby 的Time class提供一個strptime方法,可以直接定義time format,require 'time'之後就可以使用了直接舉個例子:

require 'time'

t = Time.strptime("2014-02-13 16:50:47 +0800", "%Y-%m-%d %H:%M:%S %z") 
# => 2014-02-13 16:50:47 +0800

t.class # => Time

就直接得到了Time物件了,省去了字串解析的麻煩。

如果你需要的DateTime物件,也可以依法炮製:

require 'time'

dt = DateTime.strptime("2014-02-13 16:50:47 +0800", "%Y-%m-%d %H:%M:%S %z") 
# => #<DateTime: 2014-02-13T16:50:47+08:00 ((2456702j,31847s,0n),+28800s,2299161j)>

dt.class # => DateTime 

一樣可以直接得到DateTime物件。

如此就可以從各式各樣的時間格式,直接產生TimeDateTime物件了。來試個怪怪的格式:

time = Time.strptime("22日2008年06月 16-50::23", "%d日%Y年%m月 %M-%S::%H")
# => 2008-06-22 23:16:50 +0800

呵呵 怪怪的格式也沒問題了

2014年2月12日 星期三

以Ruby語言執行系統命令

以Ruby語言去執行系統命令,有下列三種方式

  • exec args
  • system args
  • %x(args) 或 `args`

exec args

exec會中斷目前Ruby正在進行的process,然後執行系統命令。所以這個指令還蠻少用的,可以在irb裡面試試

root$ irb
2.1.0 :001 > exec 'pwd'
/Users/wemee/Documents/GitHub/test
root$

irb直接被中斷掉,然後進入bash執行pwd,所以大概只能用在linux的crontab裡面,定期執行一次就中斷

system args

system則會保留目前Ruby的process,執行完系統命令後,依照執行結果回傳: true: 執行成功 false: 執行失敗,譬如移動不存在的檔案 * nil: 執行的指令本身就打錯

system 'pwd'    # => true
system 'mv not_exist_file.txt foo.txt'  # => false
system 'wrong instruction'  # => nil 

%x(args) 或 `args`

這兩個也都不會重斷Ruby的process,並且執行完後,完整回傳系統命令回傳的字串。%x(args) 或 `args`兩種寫法執行結果都一樣,所以我都寫`args`,而且ruby-style-guide也多半認為少用%x比較好。

dir = `pwd`     # => dir = "/Users/root/projects\n"
files = `ls`    # => files = "main.rb\ntemp.txt\ntest.txt\n"

這邊可以接著用string.chmop把最後面字尾那個"\n"刪掉,或用用string.gsub("\n", " ")把所有的"\n"替換成空白欄位。


最後,執行這些指令之前,記得檢查一下你的系統唷,很多linux的指令,跟windows上面的指令不同,回傳值也不同。如果一定要用的話,最好去先找找官方的函式庫有沒有可以利用的,或到GitHub上面找找有沒有人寫出可以跨平台的功能了。

2014年2月7日 星期五

Rails, 上傳圖片或其他檔案

目前網路上關於如何在Ruby on Rails上傳檔案的方式,最熱門的就屬paperclip

它處理圖檔必須用到convert指令,如果是Linux系統可以用which convert這個指令查看一下,系統裡面有沒有convert指令。如果沒有的話paperclip是推薦用ImageMagick,如果你有要上傳圖檔的話,就安裝一下吧,Linux可以用rpm安裝,MacOS就直接brew install imagemagick

之後就使用gem安裝paperclip,gem "paperclip"

簡易使用方式

我是使用Rails 4,以下是Rails 4的場景。

產生資料庫欄位的部分它沒有實作在rails generate指令裡面,所以必須自己先產生Model, 然後再產生一個Migration,把欄位加上去之後,做rake db:migrate,例如:

# 產生 "image" Model, 以及一個欄位"title"
rails g model image title:string

打開所產生的model檔案(app/models/image.rb),為它增加一個file欄位關聯

class Image < ActiveRecord::Base
  has_attached_file :file, 
                    styles: {medium" "300x300>", thumb: "100x100>" }, 
                    default_url: "/images/:style/missing.png"

  validates_attachment_content_type :file, content_type: /\Aimage\/.*\Z/
end

但是只是先設定好Model關聯而已,我們實際上資料庫Table裡面並沒有file欄位,我們利用migration為他增加資料庫的file欄位:

# 產生一個migration 準備增加file欄位
rails g migration image_add_column_file

開始修改產生的migration檔案

class ImageAddColumnFile < ActiveRecord::Migration
  def self.up
    add_attachment :images, :file
  end

  def self.down
    remove_attachment :images, :file
  end
end

執行rake db:migrate

這邊要注意,因為我們習慣在migration裡面,使用change函式,讓migarate自動決定up與down的做法,譬如up做add_column,那麼down就做remove_column。

但rails裡頭並沒有內建add_attachment相對是remove_attachment,所以這邊我們自己保險一點,up與down做什麼都定義清楚。

Rails_Admin

如果你有裝Rails_Admin,這時候進入後台就可以看到,已經可以上傳圖檔了。


Controller and View

先用Rails_Admin隨便加幾個圖片之後,接下來我們弄個Controller來玩玩

rails g controller images

路徑設定,就直接用直接RESTful

resources :images

打開images_controller.rb,先弄個index來試試

class ImagesController < ApplicationController
  def index
    @image = Image.first
  end
end

直接取出第一筆資料 在images/index.html.erb裡面顯示看看

<%= image_tag @image.file.url %>
<%= image_tag @image.file.url(:medium) %>
<%= image_tag @image.file.url(:thumb) %>

效果:


上傳圖片,讀出圖片都沒問題,其他就是Rails基本功的問題囉。至於paperclip其他部分,有研究在繼續Update本篇文章下面

2014年2月6日 星期四

Ruby 併行賦值(Parallel Assignment)

前言

原本在找Ruby要怎麼交換兩個陣列的位置,找到一個很神奇的方法

class Array
    def swap!(a,b)
        self[a], self[b] = self[b], self[a]
        self
    end
end

完全看不懂在幹嘛,如果是C語言,在同一行裡使用逗號,則表示從右到左依次執行而已,例如:

int i,j;
for(i=0,j=5;i<10;i++,j--);

在C語言這個迴圈共執行10次,每次都執行i++與j--,我把i++與j--寫在一起用逗號分開,其實也可以這麼寫

int i,j;
for(i=0,j=5;i<10;){
    i++;
    j--;
}

有時候為了簡潔會塞在同一行。

可是Ruby這麼做,我全看不懂在幹嘛,在文章開頭的swap!函式裡頭,self[a]在我眼裡就是回傳self[a],然後將self[b]賦值self[b]等於沒變,最後再回傳self[a]而已,完全不清楚這是怎麼swap。

Parallel Assignment

查了一下ruby-doc.com之後,找到了Parallel Assignment這個關鍵字,終於搞清楚了,Ruby會將等號右邊所有的物件,按照相對位置賦值到等號左邊的參數,就叫做Parallel Assignment,譬如你要將x,y,z分別賦值1,2,3,在C語言你可能這麼寫

int x=1, y=2, z=3;

在Ruby裡面可以這麼寫

x,y,z = 1,2,3

就是如此而已,x,y,z就分別被賦值1,2,3。

當然,像我這種宅宅,一定還會好好地玩弄一下,我故意讓等號兩邊無法成對:

1: a,b = 1,2,3     # a=1, b=2
2: i,j,k = 1,2     # i=1, j=2, k=nil

行1,我原本以為b會被賦值為陣列[2,3],其實不用這麼古怪,Ruby設計直接把多出來的3丟掉,聰明的設計。

行2,我原本以為會出現錯誤訊息,但這也太嚴謹了,Ruby直接讓k=nil就行了,也是聰明的設計。

接著繼續惡搞一下,看他的語法有沒有靈活性:

a,b = c=1, 2            # a=1, b=2, c=1
a,b = c,d = 1, 2, 3     # Compiler Error
# 編譯錯誤 所以自己加上括號
a,b = (c,d = 1, 2), 3   # a=[1,2], b=3, c=1, d=2

有趣唷Parallel Assignment會回傳一個陣列,所以很靈活

SWAP

既然如此,我們就可以利用Parallel Assignment,來進行簡潔的swap,將x,y兩數交換:

# Parallel Assignment 定初值
x,y = 1,2   # x=1, y=2

# 一樣用Parallel Assignment技巧交換兩數
x,y = y,x # x=2, y=1

你想要3個數字一起交換也可以

x,y,z = 1,2,3   # x=1, y=2, z=3
x,y,z = y,z,x   # x=2, y=3, z=1

搞清楚之後,文章開頭提的那個陣列交換swap!函式就看得懂了

def swap!(a,b)
    self[a], self[b] = self[b], self[a]
    self
end

簡單將a,b兩個位置交換之後,最後把回傳陣列本身

2014年2月5日 星期三

費氏數列(Fibonacci)

Fibonacci on wiki

使用遞回

/*
 * @parama n 第n項(n為正整數)
 * @return 第n項的值
 */
function fibonacci(n)
    if n <= 1
        return n
    return fibonacci(n-1) + fibonacci(n-1)
end

使用迭代

/*
 * @parama n 第n項(n為正整數)
 * @return 第n項的值
 */
function fibonacci(n)
    if n <= 1
        return n
    int a=0, b=1,t;
    for i = 2 to n do
        t = a+b
        a = b
        b = t
    end
    return b
end

使用cache優化遞回

/*
 * @parama n 第n項(n為正整數)
 * @return 第n項的值
 */
function fibonacci(n)
    static Array cache ||= {0,1}
    if cache[n] not null
        return cache[n]
    cache[n] = fibonacci(n-1) + fibonacci(n-1)
    return cache[n]
end

我在Github上使用Ruby的實作

Fork me on github

Markdown to HTML(Maruku)

Maruku on Github

本文網址

純Ruby做成,可將Markdown語法轉換成HTML,latex,md...等格式,以下簡單做點介紹。

安裝

gem install maruku

先在irb下面做點測試

require 'Maruku'            # => true

# 設定Markdown字串
markdown_string = '#大標題'

# 使用Markdown字串 建立Maruku物件
doc = Maruku.new(markdown_string)

#從Maruku物件輸出HTML文檔
doc.to_html                 # => "\n<h1 id=\"\">大標題</h1>\n" 

#從Maruku物件輸出latext文檔
doc.to_latext_document      # => "\\documentclass{article}\n\n% ... 

至於還有哪些Methods 自己用Ruby內建的methods語法來顯示

Maruku.methods              # => 一堆class methods
Maruku.new.methods          # => 一堆instance methods

可以再加上正規表示式來尋找

Maruku.new.methods.grep(/html/)  # => 一堆HTML相關的instance methods
Maruku.new.methods.grep(/latex/) # => 一堆latex相關的instance methods

與Ruby on Rails結合

在Gemfile裡面加上

gem 'maruku'

譬如你自己做個Blog,讓人使用Markdown語法來寫文章。文件從資料庫取出來,直接傳給view顯示就行了,例如文章儲存在Post資料表的content欄位

Markdown_string = Post.first.content
@doc = Maruku.new(Markdown_string)

在view的部分找個地方直接

<span>@doc.to_html.html_safe</span>

記得要使用html_safe,不然只會出現一堆&lt ; &gt ; 的符號,當然你自己得確定從Markdown轉出來的HTML是安全的(至少我目前還沒發現,要怎麼在Markdown裡面塞javascript,所以都當它們都是安全的)

Code Block for Blogger

Code Block for Blogger

我使用Markdonw語法打部落格文章,先轉換成Html之後,在貼到Blogger去,但是闡述程式碼的Code Block的區塊,產生出來的HTML是

<pre><code> ... </pre></code> 

這些HTML貼到Blooger去,因為沒有CSS樣式裝飾,所以只有簡單的換行縮排,看起來有點像引言

這邊我們可以自己加上CSS Style上去,讓他看起來漂亮點。首先Markdown轉出來的code block有兩種形式,一種是嵌入式的,譬如-> int i=0; <- 直接嵌入在行內,另一種是整個程式碼區塊,譬如:

int i=0;    /* 程式碼自己佔一個區塊 */

嵌入式的程式碼區塊產生的HTML會是`int i=0;

整個程式碼區塊產生的HTML會是`

int i=0;

差在<pre>tag的有無,所以我們只要分別加上這兩個CSS Style給blooger的範本,就行了。可以自己寫,不過我直接在網路上找別人做好的:

<style>
code {
background-color: #F9F9F9;
border: 1px dashed #2F6FAB;
color: black;
/* line-height: 1.1em; 
padding: 1em;*/
}

pre code {
display: block; /* fixes a strange ie margin bug */
font-family: Courier New;
font-size: 10pt;
overflow:auto;
background: #f0f0f0 url(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAASwCAYAAAAt7rCDAAAABHNCSVQICAgIfAhkiAAAAQJJREFUeJzt0kEKhDAMBdA4zFmbM+W0upqFOhXrDILwsimFR5pfMrXW5jhZr7PwRlxVX8//jNHrGhExjXzdu9c5IiIz+7iqVmB7Hwp4OMa2nhhwN/PRGEMBh3Zjt6KfpzPztxW9MSAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzB8HS+J9kUTvzEDMwAAAABJRU5ErkJggg==) left top repeat-y;
border: 1px solid #ccc;
padding: 10px 10px 10px 21px;
max-height:1000px;
line-height: 1.2em;
}
</style>

把上面一串貼到你的Blogger範本去,進入編輯HTML,放在<head>裡面就行了。


這個CSS將兩個HTML tag <code><pre><code> 加上了一些裝飾。

如果在網路上找相關的CSS style樣式表,大部份別人的CSS style樣式表是內容大約是這樣

.post .codeblock { ... }

事實上這樣比較正確,在指定的程式碼區塊Tag加上class=codeblock,才會套用此CSS樣式。此法可以將Markdown產生的程式碼區塊,與非Markdown產生的<code></code>鬆綁。

但我懶得Markdown轉換後還要一個一個加上class name,而且我貼到Blogger去的文章一定都是我用Markdown打的,所以就直接CSS指定<code><pre><code>區塊都套用CSS。