Rodhos Soft

備忘録を兼ねた技術的なメモです。Rofhos SoftではiOSアプリ開発を中心としてAndroid, Webサービス等の開発を承っております。まずはご相談下さい。

hello express

expressをinstallしておく

npm install express

typeも

npm install @types/express
import express from "express";
var app = express();

app.get("/", (req, res) => {
    return res.send("Hello World!");    
});

app.listen(3001, () => {
    console.log("app listening on port 3001");
});

これでtscでトランスパイルし、nodeで実行、localhost:3001にアクセスする。

hello node

nodeのtypeを入れておく。

npm install @types/node
import * as http from "http"



let server:http.Server = http.createServer((req:http.IncomingMessage, res:http.ServerResponse) => {
    res.writeHead(200, {'Content-Type':'text/plain'});
    res.write("Hello World!");
    res.end();
});


server.listen(3001);

console.log("Server start at http://localhost:3001/");

これでlocalhost:3001でhello worldできる。

abc

バージョン確認

node -v
v10.16.0
npm -v
6.9.0

typescriptのコンフィグファイル作成

tsc --init

npm開始

npm init

typescript実験用にhello.ts作成

console.log("hello")

トランスパイル

tsc

試しに実行してみる。

node hello.js

jsの吐き出し先をdistフォルダに変更する。tsconfigで

    "outDir": "./dist",                        /* Redirect output structure to the directory. */

のように設定しておく。

文字横に画像

試行錯誤の結果次のようになった。

+ (NSAttributedString *)backSymbol {
    
    UIImage *image = [UIImage imageNamed:@"backbutton"];
    NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
    attachment.image = image;
    NSMutableAttributedString *ret = [[NSMutableAttributedString alloc] init];
    
    UIFont *font = [UIFont systemFontOfSize:17.0];
    CGSize size = CGSizeMake(font.lineHeight * image.size.width/image.size.height, font.lineHeight);
    
    CGFloat y = round(font.capHeight - size.height)/2;
    [attachment setBounds:CGRectMake(-size.width*1/4, y, size.width, size.height)];
    
    
    
    NSAttributedString *back = [NSMutableAttributedString attributedStringWithAttachment:attachment];
    
    [ret appendAttributedString:back];
//    NSMutableAttributedString *back = [[NSMutableAttributedString alloc] initWithString:@"<"];
    [ret addAttribute:NSFontAttributeName
                 value:[UIFont boldSystemFontOfSize:21.0]
                 range:NSMakeRange(0,back.length)];
    return ret;
}

参考

Swift5 UILabelに画像(UIImage)を表示する NSAttributedString NSTextAttachment - Qiita

A-Liaison BLOG: iOSのフォントのお話

delayする実験

#include <iostream>
#include "rx-includes.hpp"

class Man {
    public:
        Man();
        ~Man();

        void hello();

    private:
        int age;
        std::string name;

};

template <typename T>
class AbstBase {
    public:
    T hoge;
};

class FuncClass {
    public:
        FuncClass() : connectStateOb(std::shared_ptr<rxcpp::subjects::behavior<bool>>::make_shared(true)){

        };
        ~FuncClass(){}
        std::shared_ptr<rxcpp::subjects::behavior<bool>> connectStateOb;
        void poi();
        rxcpp::observable<std::string> connect();
        rxcpp::observable<std::string> disscconnect();
};

class Base : public AbstBase<Man>, public FuncClass
{
public:
    Base() : FuncClass(){};
    ~Base(){};
};

class Conc: Base {
    public:
        Conc(): Base(){

        };
        
        ~Conc(){
            std::cout << " ** deconstruction Conc **" << std::endl;
        };
        rxcpp::observable<std::string> rxtest();
        void log(std::string txt);
};
#include "man.hpp"
#include <chrono>

Man::Man(): name("hoge"), age(0) {

}

Man::~Man() {
    std::cout << " ** desconstructe Man **" << std::endl;
}

void Man::hello() {
    std::cout << " ** hello **" << std::endl;
}

void FuncClass::poi()
{
    std::cout << " ** poi **" << std::endl;
}

rxcpp::observable<std::string> FuncClass::connect() {
    rxcpp::observable<int> a = rxcpp::observable<>::just(0)
                                   .delay(std::chrono::milliseconds(1000));

    rxcpp::observable<std::string> b = a.flat_map([=](auto x) {
                  rxcpp::observable<std::string> c = rxcpp::observable<>::just<std::string>(std::string("event!"));
                  return c;
              }).as_dynamic();

    return b;
}

rxcpp::observable<std::string> FuncClass::disscconnect() {
    auto v = rxcpp::observable<>::interval(std::chrono::milliseconds(1000), rxcpp::observe_on_new_thread())
                 .map([=](long x) {
                     return std::to_string(x);
                 })
                 .take(2);
    return v;
}

void Conc::log(std::string txt) {
    std::cout << txt << std::endl;
}

rxcpp::observable<std::string> Conc::rxtest() {
    auto o = rxcpp::observable<>::just(10).as_dynamic();
    rxcpp::observable<std::string> s = o.flat_map([=](int x) {
                  return connect();
              })
                 .as_dynamic()
                 .flat_map([=](std::string c) {
                     std::cout << "==>" << c << std::endl;
                     log("log1");
                     return disscconnect();
                 })
                 .as_dynamic()
                 .flat_map([=](std::string d) {
                     log("log2");
                     log(d);
                     this->hoge.hello();
                     return rxcpp::observable<>::just(d);
                 })
                 .as_dynamic();

    return s;
}

操作を遅らせたりして動作を確認していた。

void rxtest2() {
    std::shared_ptr<Conc> conc = std::shared_ptr<Conc>::make_shared();
    rxcpp::observable<std::string> x =  conc->rxtest();
    auto l = x.subscribe([=](auto r) {
        std::cout << "** get event **" << std::endl;
        std::cout << r << std::endl;
    });

    conc.reset();

    std::async(std::launch::async, [l]() {
        usleep(5 * 1000 * 1000);
        std::cout << "** unsubscribe **" << std::endl;
        l.unsubscribe();

        

        usleep(5 * 1000 * 1000);
        std::cout << "** end **" << std::endl;

    });
}

色々やっているうちにコードが無駄に複雑化したがオブジェクトのデストラクタのタイミング等が少し理解できた。

分割コンパイル

hojo.hpp

int hello();

hojo.cpp

#include "hojo.hpp"

int hello() {
    return 1000;
}

hello.cpp

#include <iostream>
#include "hojo.hpp"

using namespace std;

int main(){
  cout << "Hello world." << endl;
  int x = hello();
  cout << x << endl;
  return 0;
}

というファイルをコンパイルしたいとする。 次のようなMakefileを作る。

Makefile

objs = hello.o hojo.o

CC = clang++

hello: $(objs)
    $(CC)  -o hello $(objs)

hello.o:hello.cpp
    $(CC) -c -o hello.o hello.cpp
hojo.o:hojo.cpp
    $(CC) -c -o hojo.o hojo.cpp

clean:
    rm -f $(objs)

これでmakeすればビルドできる。

CMakeで同様の事をする場合

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(hello)
add_executable(hello hello.cpp hojo.cpp)

を作り、

mkdir build
cd build
cmake ..
make

でビルドできる。

そのご 他のフォルダをインクルードした

message("hoge")
cmake_minimum_required(VERSION 3.1)
enable_language(CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

project(hello)
include_directories(/Users/hoge/RxCpp-4.0.0/Rx/v2/src)
# set(CMAKE_MODULE_PATH "usr/local/rxcpp" ${CMAKE_MODULE_PATH})
message("INCLUDE_DIRECTORIES ${INCLUDE_DIRECTORIES}")
message("CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}")
# find_package(rxcpp)
add_executable(hello hello.cpp hojo.cpp)

最低限webpackを使う

node package.config

{
  "name": "hoge",
  "version": "1.0.0",
  "description": "hoge",
  "main": "index.js",
  "scripts": {
    "build": "webpack",
    "watch": "webpack -w",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "KatagiriSo",
  "license": "ISC",
  "dependencies": {
    "ts-loader": "^6.1.0",
    "typescript": "^3.6.3",
    "webpack": "^4.39.3",
    "webpack-cli": "^3.3.8"
  }
}

typescriptのtsconfig.json

{
  "compilerOptions": {
    /* Basic Options */
    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
    "module": "es2015",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "lib": ["es2019", "dom"],                             /* Specify library files to be included in the compilation. */
    // "allowJs": true,                       /* Allow javascript files to be compiled. */
    // "checkJs": true,                       /* Report errors in .js files. */
    // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
    "sourceMap": true,                     /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    // "outDir": "./",                        /* Redirect output structure to the directory. */
    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                     /* Enable project compilation */
    // "removeComments": true,                /* Do not emit comments to output. */
    // "noEmit": true,                        /* Do not emit outputs. */
    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true,                           /* Enable all strict type-checking options. */
    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
    // "strictNullChecks": true,              /* Enable strict null checks. */
    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
    // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */

    /* Module Resolution Options */
    "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                       /* List of folders to include type definitions from. */
    // "types": [],                           /* Type declaration files to be included in compilation. */
    // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true                   /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */

    /* Source Map Options */
    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
  }
}

webpackの webpack.config.js

module.exports = {
    mode: "development",
    entry: "./src/main.ts",

    module: {
        rules: [
            {
                test: /\.ts$/,
                use: "ts-loader"
            }
        ]
    },
    resolve: {
        extensions: [".ts"]
    }
};

node for mobile調べ

GitHub - JaneaSystems/nodejs-mobile: Full-fledged Node.js on Android and iOS

AndroidはV8 JavaScript engineをつかう。
iOSは略

sampleがあるので
フレームワークをそのsampleに入れるだけで動く。
NodeRunnerというクラスがあるので、それにjsのソースのパスを食わせる。

canvasのimageData

 const imagedata:ImageData = context.createImageData(sw,sh)

sw,sh 単位はCSSピクセル ImageDataを作成して返却 返却されたピクセルはすべて透明な黒

sw,shの代わりにimagedataを渡しても良い。 その場合、渡したimagedataの寸法の 新たなImageDataが作成される。すべて透明な黒

 const imagedata:ImageData = context.getImageData(sx,sy,sw,sh)

canvasの指定矩形のimagaDataを返す。

引数が有限でもないか0であればNOT_SUPPORTED_ERR , INDEX_SIZE_ERR を投げる。

imagedata.width
imagedata.height

データの実際の寸法で単位はピクセル

imagedata.data

RGBAの順番で1次元配列で0 ~ 255の範囲

context.putImageData(imagedata, dx, dy [, dirtyX, dirtyY, dirtyWidth, dirtyHeight ])

imageDataを描画、dirtyを与えると特定の矩形のピクセルだけ描画

ImageData

interface ImageData {
    /**
     * Returns the one-dimensional array containing the data in RGBA order, as integers in the
     * range 0 to 255.
     */
    readonly data: Uint8ClampedArray;
    /**
     * Returns the actual dimensions of the data in the ImageData object, in
     * pixels.
     */
    readonly height: number;
    readonly width: number;
}

canvasのコンテキスト

    getContext(contextId: "2d", contextAttributes?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D | null;

CanvasRenderingContext2DSettingsの設定

interface CanvasRenderingContext2DSettings {
    alpha?: boolean;
}

alphaはcanvasにアルファ値が含まれているかどうか、falseに指定するとブラウザは背景が不透明であることがわかるので描画が高速化される。

状態モナドを作ってみる

typescriptで状態モナドを作ってみます。

状態モナド

 S \to (R,S)
という状態を受け取って結果と変化した状態を返す関数です。

これを

export type State<R,S> = (s:S) => {result:R,state:S}

と定義しておきます。

初期値を作る関数を

export const unit = <R,S>(result:R) => (state:S) => { return {result:result, state:state}}; 

と定義します。これで

    const m0 = unit<number,number>(100);

のように初期入力値を作ることができます。

次に数を2倍する関数を作りましょう。そして関数が呼ばれる回数を状態としてカウントすることを考えます。
具体的には

    const f = (r:number) => (s:number) => { return {result:r*2, state:s+1}}

のような関数を用意します。つまり、状態を受け取ったら値を2倍しつつ状態を更新して返すという関数です。
これは

    const f: (number) =>State<number, number>

という型を持っていることに注意しましょう。

作っておいた状態モナドm0にこの関数を適用するための関数としてbindを用意します。

export const bind = <R,S,Q>(m:State<R,S>, f:(r:R)=>State<Q,S>) => (s:S) => {
    const next = m(s);
    return f(next.result)(next.state);
}

これは新しく状態モナドを作って、その状態モナドでは、元の状態モナドを先に使って結果を求め、それにfを適用するようにしてあります。

bindを使って、m0にfを3回くらい適用させてみましょう。

    const m0 = unit<number,number>(100);
    const m1 = bind(m0,f);
    const m2 = bind(m1,f);
    const m3 = bind(m2,f);

m1,m2を用いたのはわかりやすさのためで、bindを連続して作用させても構いません。

できた結果の状態モナドm3を使うには初期状態を設定する必要があります。

    const ans = m3(0)

ans.result == 800
ans.state == 3
となり、きちんとカウントしていることがわかると思います。

状態モナドの説明はこれで終わりです。
色々な使い方がありそうだということがわかると思います。

以上をまとめたコードがこちらになります。

export type State<R,S> = (s:S) => {result:R,state:S}

export const unit = <R,S>(result:R) => (state:S) => { return {result:result, state:state}}; 
export const bind = <R,S,Q>(m:State<R,S>, f:(r:R)=>State<Q,S>) => (s:S) => {
    const next = m(s);
    return f(next.result)(next.state);
}

export function stateTest() {
    /// double number , state: count of operation
    const f = (r:number) => (s:number) => { return {result:r*2, state:s+1}}

    const m0 = unit<number,number>(100);
    const m1 = bind(m0,f);
    const m2 = bind(m1,f);
    const m3 = bind(m2,f);
    
    const display = <R>(m:State<R,number>) => {
        console.log("result:"+m(0).result + " state:"+m(0).state);
    }

    display(m0);
    display(m1);
    display(m2);
    display(m3);

    /*
        result:100 state:0
        result:200 state:1
        result:400 state:2
        result:800 state:3
    */

    
}

stateTest();

ありがとうございました。