common lisp マクロ入門

lispを少しずつ勉強しています。
マクロを書いてみました。

(defmacro list3 (a b c)
  `'(a b c))

(defmacro let1 ((var val) &body body)
  `(let ((,var ,val))
     ,@body
     ))

(defmacro -unless (bool &body body)
  `(if (not ,bool)
     (progn
       ,@body)
     nil))

(print (macroexpand-1 '(list3 :a :b :c)))
(print (macroexpand-1 '(let1 (c 2) (print c))))
(print (macroexpand-1 '(-unless (= 1 1) (print "HI"))))

フロントエンドというかhtml, cssが嫌いなので
lispのマクロで苦にならないようなものを作りたいです。

なんかちょっと楽しいぞ( ̄ー ̄)

common lisp の first car

最近common lispをより深く知りたくて
趣味で作っているアプリのサーバー部分をcommon lispで書こうとしています。

通勤時間に「実践Common lisp」を読んでいるのですが、
car, cdrは古いみたいですね。
これからはfirst, restを使っていきたいと思います。

サーバーはcaveman2を使ってみたいと思います。
フロントはunityです。

時間があれば、そっち側もブログに書けたらいいなと思っています。
もうすでにcaveman2動かすまでかなりの時間を使ってます笑

rubyでgem公開してみた。

rubyで初めてgemを公開してみました。

github.com


rubytrisというテトリスのgemです。
オープンソースの開発とかも携わっていけたらいいな.

phpで使いそうな画像処理のクラス2

前回のコードが時間がないままフレームワークの中で使っていたものを汎用的にしたので
最悪だったのですが
今回はまだマシかと思います。
とりあえず直しただけ多分動く

一つ目は戒めとして残しておきます。

<?php
class ImageChanger{
      const FILE_MAX_LENGTH = 750;
      const COMPRESSION = 60;

      private $target_file;
      private $width;
      private $height;
      private $type;

      function __construct($target){
            $this->target_file = $target;
      }

      /* public method */
      public function getFile(){ return $this->target_file; }

      //画像の縮小
      public function createImage(){ //->bool
            if(file_exists($this->target_file) == false){
                  return false;
            }
            $this->getImageInformation();
            $size = $this->imageSize(self::FILE_MAX_LENGTH);
            //型作り
            $new_image = imagecreatetruecolor($size['width'], $size['height']);

            switch($this->type){
            case 'image/gif':
                  $base_image = imagecreatefromgif($this->target_file);
                  break;
            case 'image/png':
                  $base_image = imagecreatefrompng($this->target_file);
                  break;
            case 'image/jpeg':
            case 'image/jpg':
                  $base_image = imagecreatefromjpeg($this->target_file);
                  break;
            default:
                  return false;

            }
            //型に合わせて作成
            imagecopyresampled($new_image, $base_image, 0, 0, 0, 0, $size['width'], $size['height'],  $this->width, $this->height);
            ImageDestroy($base_image);

            //元データの削除
            if(file_exists($this->target_file)){
                  unlink($this->target_file);
                  $this->target_file = "";
            }
            $file_path = $this->getNewImagePath();

            imagejpeg($new_image, $file_path, self::COMPRESSION);
            ImageDestroy($new_image);

            $this->target_file = $file_path;
            return true;
      }

      //画像の向きを元に戻す
      public function rotateImage(){ //->bool
            if(file_exists($this->target_file) == false){
                  return false;
            }
            $this->getImageInformation();

            if($this->type != "image/jpeg" && $this->type != "image/jpg"){
                  return true;
            }
            //型作り
            $new_image = imagecreatetruecolor($this->width, $this->height);

            $exif = $this->exifInformation($this->target_file);
            if($exif == null){
                  return false;
            }

            $base_image = imagecreatefromjpeg($this->target_file);
            if(isset($exif['IFD0']['Orientation'])){
                  //回転処理
                  $rotate_infos = $this->imageRotation($exif['IFD0']['Orientation']);

                  //反転
                  if(!empty($rotate_infos['mode'])){
                        $base_image = imageflip($base_image, $rotate_infos['mode']);
                  }

                  //回転
                  if($rotate_infos['degrees'] > 0){
                        $base_image = imagerotate($base_image, $rotate_infos['degrees'], 0);
                  }
            }

            //元データの削除
            if(file_exists($this->target_file)){
                  unlink($this->target_file);
                  $this->target_file = "";
            }
            $file_path = $this->getNewImagePath();

            imagejpeg($base_image, $file_path, self::COMPRESSION);
            ImageDestroy($base_image);

            $this->target_file = $file_path;
            return true;
      }


      /* private method */
      //同じ縦横比のまま長いほうが指定した数値になるように縦横を返す
      private function imageSize($max_length){ // -> [int, int]
            if($this->width < $max_length || $this->height < $max_length){
                  $w = $this->width;
                  $h = $this->height;
            }elseif($this->width > $this->height){
                  $ratio = $this->height / $this->width;
                  $w = $max_length;
                  $h = round($w * $ratio);
            }elseif($this->width < $this->height){
                  $ratio = $this->width / $this->height;
                  $h = $max_length;
                  $w = round($h * $ratio);
            }else{
                  $w = $max_length;
                  $h = $max_length;
            }
            $size = ['width' => $w, 'height' => $h];
            return $size;
      }

      private function getNewImagePath(){ // -> string
            $name = str_replace('.', '', microtime(true));
            $filepath = 'tmp/'. $name. '.jpeg';
            return $filepath;
      }

      //file 情報取得
      private function getImageInformation(){ //->void
            $infos = getimagesize($this->target_file);
            $this->width = $infos[0];
            $this->height = $infos[1];
            $this->type = $infos['mime'];
      }


      //exif情報取得
      private function exifInformation(){
            if(file_exists($this->target_file) == false) return null;
            $exif = exif_read_data($this->target_file, 0, true);
            if($exif === false){
                  return null;
            }else{
                  return $exif;
            }
      }

      //イメージの回転修正
      //ファイルのパスをもらって回転の処理に必要な$mode, $degreesを返す
      private function imageRotation($exif){ //-> [string, int]
            if(isset($exif) == false){
                  return ["", 0];
            }
            $degrees = 0;
            $mode = '';

            switch($exif){
            case 1:
                  break;
            case 2:
                  $mode = 'IMG_FLIP_HORIZONTAL';
                  break;
            case 3:
                  $degrees = 180;
                  break;
            case 4:
                  $mode = 'IMG_FLIP_VERTICAL';
                  break;
            case 5:
                  $degrees = 90;
                  $mode = 'IMG_FLIP_HORIZONTAL';
                  break;
            case 6:
                  //$degrees = 90;
                  $degrees = 270;
                  break;
            case 7:
                  $degrees = 90;
                  $mode = 'IMG_FLIP_VERTICAL';
                  break;
            case 8:
                  $degrees = 270;
                  break;
            }

            return ['mode' => $mode, 'degrees' => $degrees];
      }

}

haskellでバブルソート

haskell面白いです。

なんかアプリとか作ってみようかな

main :: IO ()
main  = do
  putStrLn $ (show pre_sort ) ++ " bubbleSorted -> " ++ (show (bubbleSort pre_sort))
  where
    pre_sort = [6, 5, 1, 9, 3, 8, 2, 7, 4]

bubbleSort :: Ord a => [a] -> [a]
bubbleSort [] = []
bubbleSort [a] = [a]
bubbleSort lst = x : (bubbleSort xs)
  where
    (x:xs) = reverse $ bubbleSortSwap lst

bubbleSortSwap :: Ord a => [a] -> [a]
bubbleSortSwap [] = []
bubbleSortSwap [a] = [a]
bubbleSortSwap (x:y:xs)
  | x < y = y : (bubbleSortSwap (x:xs))
  | otherwise = x : (bubbleSortSwap (y:xs))

Haskellでlifeゲームしてみた

haskell でlifegameをして見ました。
haskellですがlifegameを書くだけでここまで楽しいとは思いませんでした。

すごく楽しいですよ。
まだまだ何も詳しくないですがおすすめです。

いっぱいhaskellで書いていきたいですね!
最初はlogが出せないと思って、debugが難しかったのですが
慣れてくると関数型だからこそやりやすくもなって来ますね。

これからどんどん書いていきます!

import System.Random
-- import Debug.Trace

count :: Int
count = 20

pioneer :: Int
pioneer = 100

-- enum
data Status = Live | Death deriving (Show, Enum, Eq)

type Line = [Status]
type Field = [Line]

newLine :: Line
newLine = replicate count Death

newField :: Field
newField = replicate count newLine

-- fieldの描画
writeField :: Field -> IO ()
writeField [] = putStrLn ""
writeField f = do
  writeLine $ head f
  putStrLn ""
  writeField $ tail f

-- lineの描画
writeLine :: Line -> IO ()
writeLine [] = putStr ""
writeLine (Live:x) = do
  putStr "o "
  writeLine x
writeLine (Death:x) = do
  putStr "_ "
  writeLine x

--セルの振る舞いを決める
decideLifes :: Field -> Field -> [[Int]]  -> Field
decideLifes _ t [] = t
decideLifes f t ([x, y]:xs) = decideLifes f (decideLife f t x y) xs

decideLife :: Field -> Field -> Int -> Int -> Field
decideLife f t x y
  | is_exists && ( isUnderPopulation neighbor_count || isOverPopulation neighbor_count ) = deadCells t x y
  | (not is_exists) && isJustPopulation neighbor_count = liveCells t x y
  | otherwise = t
    where
      neighbor_count = getNeighborsCount f x y
      is_exists = isExist f x y

-- 指定されたセルを生き返らせる
liveCells :: Field -> Int -> Int -> Field
liveCells f x y = do
  front ++ [(liveCell line x)] ++ back
    where
      front = take y f
      line = drop y $ take (y+1) f
      back = drop (y+1) f

liveCell :: Field -> Int -> Line
liveCell [l] x = do
  front ++ [Live] ++ back
    where
      front = take x l
      back = drop (x+1) l


-- 指定されたcellの削除
deadCells :: Field -> Int -> Int -> Field
deadCells f x y = do
  front ++ [(deadCell line x)] ++ back
    where
      front = take y f
      line = drop y $ take (y+1) f
      back = drop (y+1) f

deadCell :: Field -> Int -> Line
deadCell [l] x = do
  front ++ [Death] ++ back
    where
      front = take x l
      back = drop (x+1) l

-- 周囲の数を調べる
getNeighborsCount :: Field -> Int -> Int -> Int
getNeighborsCount f x y = do
  foldr (+) 0 $ map trueToOne [leftup, up, rightup, left, right, leftbottom, bottom, rightbottom]
    where
      leftup = (isNotOutOfField (x-1)) && (isNotOutOfField(y-1)) && (isExist f (x-1) (y-1))
      up = (isNotOutOfField (y-1)) && (isExist f x (y-1))
      rightup = (isNotOutOfField (x+1)) && (isNotOutOfField(y-1)) && (isExist f (x+1) (y-1))
      left = (isNotOutOfField (x-1)) && (isExist f (x-1) y)
      right = (isNotOutOfField (x+1)) && (isExist f (x+1) y)
      leftbottom = (isNotOutOfField (x-1)) && (isNotOutOfField (y+1)) && (isExist f (x-1) (y+1))
      bottom = (isNotOutOfField (y+1)) && (isExist f x (y+1))
      rightbottom = (isNotOutOfField (x+1)) && (isNotOutOfField (y+1)) && (isExist f (x+1) (y+1))
      trueToOne q = if q == True then 1 else 0

-- 過疎
isUnderPopulation :: Int -> Bool
isUnderPopulation count = count == 0 || count == 1

-- 過密
isOverPopulation :: Int -> Bool
isOverPopulation count = count >= 4

-- 子作り最適
isJustPopulation :: Int -> Bool
isJustPopulation count = count == 3

--存在している
isExist :: Field -> Int -> Int -> Bool
isExist f x y = getCell f x y  == Live

-- outofrangeが起きないよう
isNotOutOfField :: Int -> Bool
isNotOutOfField num = (isOverZero num) && (isUnderCount num)

isOverZero :: Int -> Bool
isOverZero num =  num >= 0

isUnderCount :: Int -> Bool
isUnderCount num = num < count

-- セル取得
getCell :: Field -> Int -> Int -> Status
getCell f x y = f !! y !! x


-- 初期段階の生存セルの決定
randomBirthday :: Field -> [[Int]]-> Field
randomBirthday f [] = f
randomBirthday f (a:b) = do
  randomBirthday (liveCells f x y) b
    where
      x = head a
      y = head $ tail a


-- リストを前から二つずつペアにする
makePair :: [Int] -> [[Int]]
makePair [] = []
makePair (a:b:c) = [a,b] : makePair c

main = do
  gen <- getStdGen
  let field = newField
      list = take pioneer [1,2..]
      new_field = randomBirthday field $ makePair $ take (pioneer*2) $ (randomRs (0,(count-1)) gen :: [Int])
  writeField new_field
  run new_field $ take 500 [1, 2..]

run :: Field -> [Int] -> IO ()
run f [] = writeField f
run f (x:xs) = do
  writeField field
  run field xs
  where
      _y = take count [0,1 ..]
      _x = take count [0,1 ..]
      pair = [[x, y]| x <- _x, y <- _y]
      tmp = f
      field = decideLifes f tmp pair

swiftでlifeゲーム

swiftを書かなくちゃいけなくなりそうなので練習しました。

lifegameです。
swift使ってるのにいつも通りterminalです。

新しい言語っぽくて描きやすい。
methodを呼び出すときは、一つでも名前付き引数にするのかな?(書かないとエラーが上がるような気がする)
どうなんだろ?

import Darwin

enum Status{
      case Live
      case Death
}

class Field {
      static let LENGTH = 30
      static let PIONEER = 300
      private var cells: [[Status]];
      init(){
            self.cells = [[Status]](repeating: [Status](repeating: Status.Death, count: Field.LENGTH), count: Field.LENGTH)
      }

      /* public function */
      public func randomBirthday(){
            let max = UInt32(Field.LENGTH)
            for _ in 0 ..< Field.PIONEER {
                  let a = Int(arc4random() % max)
                  let b = Int(arc4random() % max)
                  self._liveCell(x: a, y: b);
            }
      }

      public func run() -> Field {
            let tmp = Field()
            tmp.cells = self.cells
            for y in 0 ..< Field.LENGTH {
                  for x in 0 ..< Field.LENGTH {
                        if self._isLiveCell(x: x, y: y) {
                              tmp._liveCell(x: x, y: y)
                        }else{
                              tmp._deadCell(x: x, y: y)
                        }
                  }
            }
            return tmp
      }

      public func write() {
            var text = "";
            for line in self.cells {
                  for l in line {
                        text += (l == Status.Live) ? "o " : "_ "
                  }
                  text += "\n"
            }
            print(text)
      }

      /* private function */
      private func _isLiveCell(x: Int, y: Int) -> Bool {
            let neighbor_count = self._getNeighborsCount(x: x, y: y)
            if self._isExist(x: x, y: y){
                  return !(self._isUnderPopulation(count: neighbor_count) || self._isOverPopulation(count: neighbor_count))
            }else{
                  return self._isJustPopulation(count: neighbor_count)
            }
      }


      private func _liveCell(x: Int, y: Int){
            self.cells[y][x] = Status.Live;
      }
      private func _deadCell(x: Int, y: Int){
            self.cells[y][x] = Status.Death;
      }

      private func _getNeighborsCount(x: Int, y: Int) -> Int {
            var result = 0
            if self._isNotOutOfField(num: x-1) && self._isNotOutOfField(num: y-1) && self._isExist(x: x-1, y: y-1){ result += 1 }
            if self._isNotOutOfField(num: y-1) && self._isExist(x: x, y: y-1){ result += 1 }
            if self._isNotOutOfField(num: x+1) && self._isNotOutOfField(num: y-1) && self._isExist(x: x+1, y: y-1){ result += 1 }
            if self._isNotOutOfField(num: x-1) && self._isExist(x: x-1, y: y){ result += 1 }
            if self._isNotOutOfField(num: x+1) && self._isExist(x: x+1, y: y){ result += 1 }
            if self._isNotOutOfField(num: x-1) && self._isNotOutOfField(num: y+1) && self._isExist(x: x-1, y: y+1){ result += 1 }
            if self._isNotOutOfField(num: y+1) && self._isExist(x: x, y: y+1){ result += 1 }
            if self._isNotOutOfField(num: x+1) && self._isNotOutOfField(num: y+1) && self._isExist(x: x+1, y: y+1){ result += 1 }
            return result
      }

      private func _isUnderPopulation(count: Int) -> Bool {
            return count == 0 || count == 1
      }

      private func _isOverPopulation(count: Int) -> Bool {
            return count >= 4
      }

      private func _isJustPopulation(count: Int) -> Bool {
            return count == 3
      }

      private func _isExist(x: Int, y: Int) -> Bool {
            return self.cells[y][x] == Status.Live
      }

      private func _isNotOutOfField(num: Int) -> Bool {
            return self._overZero(num: num) && self._underCount(num: num)
      }

      private func _overZero(num: Int) -> Bool {
            return num >= 0
      }

      private func _underCount(num: Int) -> Bool {
            return num < Field.LENGTH
      }
}




func main(){
      var field = Field()
      field.randomBirthday()
      field.write()
      for _ in 0..<500 {
            usleep(UInt32(20000))
            field = field.run()
            field.write()
      }
}

main()