C – DECODE https://decode.red/blog data decode, decoder or decoded ... design of code Mon, 15 Dec 2025 06:15:00 +0000 ja hourly 1 https://wordpress.org/?v=4.7.29 C# JSON / macOS ../../../202505101906/ Sat, 10 May 2025 01:16:01 +0000 ../../../?p=1906 C#でJSONを変換するプログラムを実装しようと思ったところ、最近はほとんどmacOSを使って開発をしていたため、macで試してみました。
macOSでC#の開発というと、まず違和感を感じますが、MAUIというマルチプラットホームのライブラリが出たり、VisualStudio for Macがサポートされないことになったり、環境に変化があるため、ちょっとやっておこうと思いました。(まずはコンソールアプリの動作確認。その後MAUI)

UnityはC#
https://decode.red/net/archives/844

かなり前にWindows以外でC#を試したときは、下記のようにmonoを使いました。
http://crossframe.iiv.jp/?s=mono

VS Code / macOS を使ったC#の開発は初めてでしたので、いろいろメモすることも目的です。

インストールファイル
dotnet-sdk-8.0.408-osx-arm64.pkg (205,144,863 バイト)

VS Codeや、Xcodeはすでに入っているものとします。(要マイクロソフトまたはGithubアカウント)

VS Code機能拡張

コマンドから新規プロジェクトを作成できます。(コンソールプロジェクト)

Program.cs

class Program
{
    static void Main()
    {
        // JSON → CSV
        JsonToCsvConverter.Convert("input.json", "output.csv");

        // CSV → JSON
        CsvToJsonConverter.Convert("input.csv", "output.json");
    }
}

input.json

[
  { "Name": "Alice", "Age": "30", "City": "Tokyo" },
  { "Name": "Bob", "Age": "25", "City": "Osaka" },
  { "Name": "Charlie", "Age": "35", "City": "Kyoto" }
]

input.csv

Name,Age,City
Alice,30,Tokyo
Bob,25,Osaka
Charlie,35,Kyoto

JsonToCsv.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

class JsonToCsvConverter
{
    public static void Convert(string jsonFilePath, string csvFilePath)
    {
        var json = File.ReadAllText(jsonFilePath);
        var array = JArray.Parse(json);

        var sb = new StringBuilder();

        // ヘッダー
        var headers = ((JObject)array[0]).Properties();
        sb.AppendLine(string.Join(",", headers.Select(h => h.Name)));

        // データ
        foreach (var obj in array)
        {
            var values = ((JObject)obj).Properties().Select(p => p.Value.ToString());
            sb.AppendLine(string.Join(",", values));
        }

        File.WriteAllText(csvFilePath, sb.ToString());
    }
}

CsvToJson.cs

using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;

class CsvToJsonConverter
{
    public static void Convert(string csvFilePath, string jsonFilePath)
    {
        var lines = File.ReadAllLines(csvFilePath);
        var headers = lines[0].Split(',');

        var jsonList = new List<Dictionary<string, string>>();

        for (int i = 1; i < lines.Length; i++)
        {
            var values = lines[i].Split(',');
            var dict = new Dictionary<string, string>();

            for (int j = 0; j < headers.Length; j++)
            {
                dict[headers[j]] = values[j];
            }

            jsonList.Add(dict);
        }

        var json = JsonConvert.SerializeObject(jsonList, Formatting.Indented);
        File.WriteAllText(jsonFilePath, json);
    }
}

ファイル構成

パッケージインストールと実行

dotnet add package Newtonsoft.Json
dotnet run

ビルドはVSCodeの三角ボタンから(やり方はいろいろあり)

JSONは今後 System.Text.Jsonを使うことになりそうです。

https://learn.microsoft.com/ja-jp/dotnet/standard/serialization/system-text-json/migrate-from-newtonsoft?pivots=dotnet-9-0

次にMAUIも試してみました。(サンプルプログラムのビルドと実行)

workloadのインストール

sudo dotnet workload install maui
dotnet workload list

実行

dotnet run

参考)https://qiita.com/aqua_ix/items/ba9533d60633abe4c850

]]>
BPF Compiler Collection ../../../202302031560/ Fri, 03 Feb 2023 13:22:40 +0000 ../../../?p=1560 eBPF(extended Berkekey Packet Filter)という機能を利用したカーネルトレースや操作プログラムを作成するためのツールキット、BCCを試してみました。
OSには、通常のプログラミングが動作するユーザモードと特権をもつプログラムを実行するカーネルモードがあり、BCCを使うとBPFの仕組みを使ってカーネルモードのプログラムをカーネルのコードをビルドすることなく実行できるようです。
Pythonコードの中にC言語のコードを書き、それをコンパイルして実行するようですが、printk関数をはさんでデバッグしていたころに比べてかなり便利な設計になったていると感じました。

参考)
https://github.com/iovisor/bcc/blob/master/INSTALL.md#ubuntu—binary
https://github.com/iovisor/bcc/blob/master/examples/tracing/hello_fields.py
https://gihyo.jp/admin/serial/01/ubuntu-recipe/0690

環境)

$ uname -r
4.15.0-202-generic
$ cat /etc/os-release
NAME=”Ubuntu”
VERSION=”18.04.6 LTS (Bionic Beaver)”
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME=”Ubuntu 18.04.6 LTS”
VERSION_ID=”18.04″
HOME_URL=”https://www.ubuntu.com/”
SUPPORT_URL=”https://help.ubuntu.com/”
BUG_REPORT_URL=”https://bugs.launchpad.net/ubuntu/”
PRIVACY_POLICY_URL=”https://www.ubuntu.com/legal/terms-and-policies/privacy-policy”
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
$ grep BPF /boot/config-uname -r
CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_NETFILTER_XT_MATCH_BPF=m
CONFIG_NET_CLS_BPF=m
CONFIG_NET_ACT_BPF=m
CONFIG_BPF_JIT=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_HAVE_EBPF_JIT=y
CONFIG_BPF_EVENTS=y
CONFIG_TEST_BPF=m
$ python -V
Python 2.7.17

インストール)

$ sudo apt-key adv –keyserver keyserver.ubuntu.com –recv-keys 4052245BD4284CDD
$ echo “deb https://repo.iovisor.org/apt/$(lsb_release -cs) $(lsb_release -cs) main” | sudo tee /etc/apt/sources.list.d/iovisor.list
$ sudo apt-get update
$ sudo apt-get install bcc-tools libbcc-examples linux-headers-$(uname -r)

hello.py

from bcc import BPF
from bcc.utils import printb
import pprint

# define BPF program
prog = """
#include <linux/sched.h>

int hello(void *ctx) {
    char comm[TASK_COMM_LEN];
    bpf_get_current_comm(&comm, sizeof(comm));
    bpf_trace_printk("Hello!: %s\\n", comm);
    return 0;
}
"""

# load BPF program
b = BPF(text=prog)
b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello")

pprint.pprint(dir(b))
pprint.pprint(vars(b))

# header
print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE"))

# format output
while 1:
    try:
        (task, pid, cpu, flags, ts, msg) = b.trace_fields()
    except ValueError:
        continue
    except KeyboardInterrupt:
        exit()
    printb(b"%-18.9f %-16s %-6d %s /%s %d" % (ts, task, pid, msg, flags, cpu))

プロセスがcloneされたときに呼び出される処理が書かれています。オリジナルから、bオブジェクトの情報表示と、明示的なタスク、追加項目の表示を加えています。
また以下、exampleにあるhello_work.pyには、”イベント名__関数名”でカーネル内部の任意の関数をフックできるしくみで書かれていますが、ここではget_syscall_fnname(“clone”)という形でかかれています。

BPF(text='int kprobe__sys_clone(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; }').trace_print()

プログラムを起動した後、別コンソールでlsコマンド、curlコマンドを実行してみました。

実行結果

$ sudo python hello.py
[‘CGROUP_DEVICE’,
‘CGROUP_SKB’,
‘CGROUP_SOCK’,
‘CGROUP_SOCK_ADDR’,
‘CLOCK_MONOTONIC’,
‘Function’,
‘KPROBE’,
‘LWT_IN’,
‘LWT_OUT’,
‘LWT_XMIT’,
‘PERF_EVENT’,
‘RAW_TRACEPOINT’,
‘SCHED_ACT’,
‘SCHED_CLS’,
‘SK_MSG’,
‘SK_SKB’,
‘SOCKET_FILTER’,
‘SOCK_OPS’,
‘TRACEPOINT’,
‘Table’,
‘XDP’,
‘XDP_ABORTED’,
‘XDP_DROP’,
‘XDP_PASS’,
‘XDP_REDIRECT’,
‘XDP_TX’,
‘__class__’,
‘__delattr__’,
‘__delitem__’,
‘__dict__’,
‘__doc__’,
‘__enter__’,
‘__exit__’,
‘__format__’,
‘__getattribute__’,
‘__getitem__’,
‘__hash__’,
‘__init__’,
‘__iter__’,
‘__len__’,
‘__module__’,
‘__new__’,
‘__reduce__’,
‘__reduce_ex__’,
‘__repr__’,
‘__setattr__’,
‘__setitem__’,
‘__sizeof__’,
‘__str__’,
‘__subclasshook__’,
‘__weakref__’,
‘_add_kprobe_fd’,
‘_add_uprobe_fd’,
‘_attach_perf_event’,
‘_auto_includes’,
‘_bsymcache’,
‘_check_path_symbol’,
‘_check_probe_quota’,
‘_clock_gettime’,
‘_decode_table_type’,
‘_del_kprobe_fd’,
‘_del_uprobe_fd’,
‘_find_file’,
‘_get_uprobe_evname’,
‘_librt’,
‘_probe_repl’,
‘_sym_cache’,
‘_sym_caches’,
‘_syscall_prefixes’,
‘_trace_autoload’,
‘add_module’,
‘attach_kprobe’,
‘attach_kretprobe’,
‘attach_perf_event’,
‘attach_raw_socket’,
‘attach_raw_tracepoint’,
‘attach_tracepoint’,
‘attach_uprobe’,
‘attach_uretprobe’,
‘attach_xdp’,
‘cleanup’,
‘debug’,
‘decode_table’,
‘detach_kprobe’,
‘detach_kprobe_event’,
‘detach_kretprobe’,
‘detach_perf_event’,
‘detach_raw_tracepoint’,
‘detach_tracepoint’,
‘detach_uprobe’,
‘detach_uprobe_event’,
‘detach_uretprobe’,
‘disassemble_func’,
‘donothing’,
‘dump_func’,
‘find_exe’,
‘find_library’,
‘fix_syscall_fnname’,
‘free_bcc_memory’,
‘funcs’,
‘generate_auto_includes’,
‘get_kprobe_functions’,
‘get_syscall_fnname’,
‘get_syscall_prefix’,
‘get_table’,
‘get_tracepoints’,
‘get_user_addresses’,
‘get_user_functions’,
‘get_user_functions_and_addresses’,
‘kprobe_fds’,
‘kprobe_poll’,
‘ksym’,
‘ksymname’,
‘load_func’,
‘load_funcs’,
‘module’,
‘monotonic_time’,
‘num_open_kprobes’,
‘num_open_tracepoints’,
‘num_open_uprobes’,
‘open_perf_events’,
‘perf_buffer_poll’,
‘perf_buffers’,
‘raw_tracepoint_fds’,
‘remove_xdp’,
‘str2ctype’,
‘support_raw_tracepoint’,
‘sym’,
‘tables’,
‘timespec’,
‘trace_fields’,
‘trace_open’,
‘trace_print’,
‘trace_readline’,
‘tracefile’,
‘tracepoint_exists’,
‘tracepoint_fds’,
‘uprobe_fds’]
{‘debug’: 0,
‘funcs’: {‘hello’: },
‘kprobe_fds’: {‘p_sys_clone’: 4},
‘module’: 94590567723712,
‘open_perf_events’: {},
‘perf_buffers’: {},
‘raw_tracepoint_fds’: {},
‘tables’: {},
‘tracefile’: None,
‘tracepoint_fds’: {},
‘uprobe_fds’: {}}
TIME(s) COMM PID MESSAGE
3859.149581000 bash 1827 Hello!: bash /…. 0
3861.768011000 bash 1827 Hello!: bash /…. 0
3861.773422000 curl 3930 Hello!: curl /…. 1
3865.956510000 bash 1827 Hello!: bash /…. 0
3865.962982000 curl 3932 Hello!: curl /…. 1

eBPFは、クラウドサービスでよく使われるコンテナ中通信(CNI:Container Network Interface)を実現するCiliumで使われているようです。
「CNIのCiliumについて調べてみた」
https://blog.framinal.life/entry/2021/02/20/222728
このあたり、また掘り下げて調べたいと思っています。

]]>
Move Semantics ../../../202212251513/ Sun, 25 Dec 2022 06:38:18 +0000 ../../../?p=1513 最近Reactive Extensionsなどで、C++を使うことが増えたことで、&&(ユニバーサル参照)を見かけたことから、ムーブセマンティクスについて興味を持ったため調べてみました。
「ReactiveX / RxCpp」
https://decode.red/net/archives/1053
C++を最初に使ったのはかなり前(まだMicrosoftのコンパイラが対応する前)でしたが、今のC++は当時の面影がないほど記法が変化しており、最近も3年ごとにアップデートされていて、ちょっと目を離すと、使わない機能などは読解が困難になるほどです。難易度が高い言語ですが、機能が豊富で実戦(いろんな意味で)では強いというメリットもあることから、最近見直しています。その一環としてムーブセマンティクス、ムーブコンストラクタといわれるものを試してみました。
所有権の移動は、最近Blockchainのブログでも触れましたが、このジャンルで重要な考え方なので興味を持ちました。
「Move / Aptos」
http://bitlife.me/bc/2022/10/23/

C++の記法は、柔軟であるゆえ複雑になる傾向があるので、まずは参考サイトのコードを自分なりにアレンジして動かしてみました。

環境)
https://wandbox.org/

参考)
https://cpprefjp.github.io/lang/cpp11/rvalue_ref_and_move_semantics.html
https://theolizer.com/cpp-school2/cpp-school2-13/#std_forward
https://marycore.jp/prog/cpp/move-constructor/

#include <iostream>
#include <string>

struct DispHelper
{
    DispHelper(char const* title, void const* ptr, std::string s)
    {
        std::cout << title << ptr << " " << s <<"\n";
    }
};
 
class Foo
{
    DispHelper	mDispHelper;
    std::string mString;
public:
    // コンストラクタ1(const参照で受け取る)
    Foo(std::string const& iString) :
        mDispHelper("Copy: iString.data()=", iString.data(), iString.data()),
        mString(iString)
    {
        std::cout << "Copy: mString.data()=";
        std::cout .operator<< (mString.data());
        std::cout << " " << mString.data() << "\n";
    }
 
    // コンストラクタ2(右辺値参照で受け取る)
    Foo(std::string&& iString) :
        mDispHelper("Move: iString.data()=", iString.data(), iString.data()),
        mString(std::move(iString))
    {
        std::cout << "Move: mString.data()=";
        std::cout .operator<< (mString.data());
        std::cout << " " << mString.data() << "\n";
    }
};
 
int main()
{
    int x = 123;
    int& lvalue = x;	//(左辺値参照型)

    int&& rvalue = 456;	//(右辺値参照型)

    std::cout << lvalue << "\n";
    std::cout << rvalue << "\n";
    
    std::string str1 = "Hello!";
    std::string str2 = std::move(str1);

    std::cout << "1:";
    std::cout .operator<< (str1.data()) << " " << str1 << "\n";
    std::cout << "2:";
    std::cout .operator<< (str2.data()) << " " << str2 << "\n";
    
    Foo f0(std::string("abcdefghijklmnop"));
 
    std::string s1("ABCDEFGHIJKLMNOP");
    Foo f1(s1);
    std::cout << "s1[" << s1.size() << "] = " << s1 << "\n";
 
    Foo f2(std::move(s1));
    std::cout << "s1[" << s1.size() << "] = " << s1 << "\n";
}

所有権を移動しただけでは、アドレスが変わらない、というテストです。ただし16文字より少ないと変化します。これは参考サイトにもありますが、SSO(Small-string optimization)によるもので、性能劣化はしないとのことです。
main関数の最初の3行は、左辺値参照と右辺値参照は、このような書き方しかできないという例です。

#include <string>
#include <cstdio>
#include <utility>
#include <stdio.h>

struct A {
  int* p;
  // ムーブコンストラクタ
  A(A&& v) : p(v.p) { printf("move\n");v.p = nullptr; }
  
  // デフォルトコンストラクタ
  A() : p(new int(123)) {printf("default\n");}
  
  // コピーコンストラクタ
  A(const A& v) : p(new int(*v.p)) {printf("copy\n");}
  
  // デストラクタ
  ~A() { printf("delete\n");delete p; }
};

int main()
{
    A a(std::move(A()));
    printf("a: %d\n", *(a.p));
    A b = a;
    printf("b: %d\n", *(b.p));

    A c;
    printf("c: %d\n", *(c.p));
    A d(std::move(c));

    printf("d: %d\n", *(d.p));

    if(c.p == nullptr){
        printf("c: null\n");
    }
    
    return 0;
}

cは、aと意味は同じですが、記法の確認のためと、移動したことの確認のため追加しました。

参考サイトでも使われていてはじめて知ったのですが、Wandbox、便利です。

]]>
Q-Learning ../../../202107171310/ Sat, 17 Jul 2021 05:10:06 +0000 ../../../?p=1310 「Q学習(Qがくしゅう、英: Q-learning)は、機械学習分野における強化学習の一種である。」
https://ja.wikipedia.org/wiki/Q%E5%AD%A6%E7%BF%92

「はじめての強化学習〜Q-learningで迷路を解く〜」
https://qiita.com/fujina_u2/items/f407a1ca143e0bbc4446

Qラーニングについて、とてもわかりやすく解説と実装されている上記サイトをみて、試してみることにしました。
コードはそのままですが、自分の理解のために詳しくコメントしてみました。

簡単に説明すると、Q値と呼ばれる行動を判定する値を、繰り返し補正しながら適正な値に調整していく仕組みです。そのため報酬と呼ばれる概念が使われます。その結果を選びやすくするわけです。
そのため結果から遡って、その過程のQ値を調整します。
このサイトでは、下のようなグラフ構造を用いて、ノード11に500とノード14に1000という報酬を与え、その経路が選びやすくなっていくことを説明しています。

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

#define NODENO 15    //Q値のノード数
#define GENMAX 1000  //学習の繰り返し回数
#define ALPHA 0.1    //学習係数
#define GAMMA 0.9    //割引率
#define EPSILON 0.3  //行動選択のランダム性を決定(閾値)
#define DEPTH 3      //道のりの深さ
#define SEED 3277

/*乱数*/
double rand1(){  //0~1
   return (double)rand()/RAND_MAX;
}
int rand01(){ //0or1
   int rnd;
   while((rnd=rand())==RAND_MAX);
   return (int)((double)rnd/RAND_MAX*2); 
}
int rand100(){ //0~100
   int rnd;
   while((rnd=rand())==RAND_MAX);
   return (int)((double)rnd/RAND_MAX*101); 
}
/*Q値表示*/
void print_Q(int idx, int *qvalue){
   printf("[%d] ", idx);
   for(int i=1 ;i<NODENO; i++) printf("%d)%d\t", i, qvalue[i]);
   printf("\n");
}

int update_Q(int s,int *qvalue){ 
   int qv;   //更新されるQ値
   int qmax; //Q値の最大値

   if(s > 6){ //最下段 : 自身の値を更新
      if(s == 14){
         qv = qvalue[s] + ALPHA * (1000 - qvalue[s]); //報酬の付与(1000)
      }
      else if(s == 11){
         qv = qvalue[s] + ALPHA * (500 - qvalue[s]); //報酬の付与(500)
      }
      else{
         qv = qvalue[s];   //そのまま
      }
   }
   else{   //最下段以外 : 下段の値を用いて更新
      if(qvalue[2 * s + 1] > qvalue[2 * s + 2]){
         qmax = qvalue[2 * s + 1]; //  *2 +1 は下の段の左
      }
      else{
         qmax = qvalue[2 * s + 2]; //  *2 +2 は下の段の右
      }
      qv = qvalue[s] + ALPHA * (GAMMA * qmax - qvalue[s]);
   }
   return qv;
}

/*行動を選択(ε-greedy)*/
int select_action(int olds,int *qvalue){
   int s;
   if(rand1() < EPSILON){ //ランダムに行動
      if(rand01() == 0){
         s = 2 * olds + 1; //  *2 +1 は下の段の左
      }
      else{
         s = 2 * olds + 2; //  *2 +2 は下の段の右
      }
   }
   else{  //ランダムに行動しない場合は、Q値最大値を選択
      if(qvalue[2 * olds + 1] > qvalue[2 * olds + 2]){
         s = 2 * olds + 1;
      }
      else{
         s = 2 * olds + 2;
      }
   }
   return s;
}

int main(){
   int qvalue[NODENO]; //Q値
   int s; //状態
   int t; //時間

   srand(SEED);

   //Q値の初期化
   for(int i=0; i<NODENO; i++){
      qvalue[i] = rand100();
   }
   print_Q(0, qvalue);

   for(int i = 0; i<GENMAX; i++){
      s = 0; //行動の初期化
      for(t=0; t<DEPTH; t++){
         s = select_action(s, qvalue); //行動選択
         qvalue[s] = update_Q(s, qvalue); //Q値の更新
      }
      print_Q(i+1, qvalue);
   }
   return 0;
}

2,5,6,11,14の値がだんだん大きくなっています。
1より2、5より6、11より14が大きく、そして11,14が報酬値に近いのがわかります。
数式で説明されると何かと難しく感じるのですが、コードにすると理解が進みます。

]]>
Frequency Modulation (2) ../../../202103271271/ Sat, 27 Mar 2021 02:22:04 +0000 ../../../?p=1271 周波数変調を使った音合成の2回目として、今回はKORG Nu:Tektブランドの製品、NTS-1を使いました。

Frequency Modulation

前回はPC上でPythonを実行しましたが、この製品は本格的なシンセモジュールで、C言語でコーディングできます。
ルックスとサイズ、それから一万円もしない価格帯からかなり過小評価していました。(同じように見えるガジェット音源が多数あるため)これは本格的なシンセサイザーの音源コア部分の開発評価ボードと思った方がいいです。エフェクタも内蔵していて音がかなり良いです。
それが普通に市販されているのもさすがKORGといったところです。さらに開発環境まで公開されています。これからのプロダクトのトレンドなのかもしれないですね。

https://www.korg.com/jp/products/dj/nts_1/
https://www.korg.com/jp/products/dj/nts_1/sdk.php
https://github.com/korginc/logue-sdk
https://korginc.github.io/logue-sdk/

このSDKを使って、前回と同様シンプルな2オペレータのシンプルFM音源を使ってみました。
ユーザ定義のバラメータを3つ使って音を変調させてみました。
ソースコードは単体を紹介しただけでは意味がありませんので、環境丸ごと下記githubに公開しましたので参照ください。


https://github.com/systemsblue/NTS-1/blob/master/sine.cpp

つまみをコントロールして変化できる未定義のパラメータは6つあり、それぞれs_state.p1からp6まで使用できます。このコードをベースとしてさらに改造していくつもりです。

オシレータというのは音源の心臓部分なのですが、このアルゴリズムの開発環境を公開することによってだれでも本格シンセサイザの部品を作ることができるという、とても興味深い製品です。

下記にパラメータ操作による音の変化を動画にしました。

]]>
Geohash ../../../20151204470/ Fri, 04 Dec 2015 14:51:15 +0000 ../../../?p=470 ジオコード(地理座標)の一つで、緯度と経度を一つの文字列で位置の範囲を表現します。

https://ja.wikipedia.org/wiki/%E3%82%B8%E3%82%AA%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5

ここでは、Googleマップで取得した緯度経度を専用サイトでGeohashに変換して、それを元の緯度経度に戻すプログラムを作成しました。Wikipediaに書かれているような、Geohashが復号される過程を確認できます。

map01
Googleマップで名古屋駅前の緯度経度を取得します。

map02
http://geohash.org/でGeohashに変換します。
下記コードで復号します。

#include

dv(double *s, double  *e, int b)
{
	if(b == 1)
		*s = (*e - *s) / 2 + *s;
	else
		*e = (*e - *s) / 2 + *s;
}
main()
{
	char str[] = "0123456789bcdefghjkmnpqrstuvwxyz";
	char h[] = "xn324fsp2vdw";

	int k1[50];
	int k2[50];

	int c = 0;
	int c1 = 0;
	int c2 = 0;

	int i = 0, j=0 , b=0;

	for(i=0;i=0 ;b--){
					int t = (j >> b) & 1;
					printf("%d", t);
					if(c % 2 == 0){
						k1[c1] = t;
						c1 ++;
					}
					else{
						k2[c2] = t;
						c2 ++;
					}
					c ++;
				}
				printf("\n");
			}
		}
	}
 	printf("%d  ", c1);
	for(i=0;i

# なぜか[ ]がエスケープされたりされなかったりして見づらいので、githubにもこのコードをアップしました。(WordPressプラグインのバグ?原因不明)

https://gist.github.com/systemsblue/5bbfd3e0f0367a608b4b

メモ追記 2015/12/5) プラグインを「Syntax Highlighter for WordPress 3.0.83.3」から 「Crayon Syntax Highlighter」に変更し、記事を修正しました。他の記事でも同様のバグを随時修正していきます。

下記、実行画面。3つにわけていますが、一気に表示されます。

$ ./geohash
x n 3 2 4 f s p 2 v d w
29:1d [x] 11101
20:14 [n] 10100
3:03 [3] 00011
2:02 [2] 00010
4:04 [4] 00100
14:0e [f] 01110
24:18 [s] 11000
21:15 [p] 10101
2:02 [2] 00010
27:1b [v] 11011
12:0c [d] 01100
28:1c [w] 11100
30 111000010101011100000001101010
30 101100100000010101110110110110

それぞれのキャラクタに該当する5bitの二進数を1bitごとに上下にわりふっていきます。上下ともに30bitになりましたが、ならなくてもいいです。

1:1) 0.000000 180.000000 90.000000
2:1) 90.000000 180.000000 135.000000
3:1) 135.000000 180.000000 157.500000
4:0) 135.000000 157.500000 146.250000
5:0) 135.000000 146.250000 140.625000
6:0) 135.000000 140.625000 137.812500
7:0) 135.000000 137.812500 136.406250
8:1) 136.406250 137.812500 137.109375
9:0) 136.406250 137.109375 136.757812
10:1) 136.757812 137.109375 136.933594
11:0) 136.757812 136.933594 136.845703
12:1) 136.845703 136.933594 136.889648
13:0) 136.845703 136.889648 136.867676
14:1) 136.867676 136.889648 136.878662
15:1) 136.878662 136.889648 136.884155
16:1) 136.884155 136.889648 136.886902
17:0) 136.884155 136.886902 136.885529
18:0) 136.884155 136.885529 136.884842
19:0) 136.884155 136.884842 136.884499
20:0) 136.884155 136.884499 136.884327
21:0) 136.884155 136.884327 136.884241
22:0) 136.884155 136.884241 136.884198
23:0) 136.884155 136.884198 136.884177
24:1) 136.884177 136.884198 136.884187
25:1) 136.884187 136.884198 136.884193
26:0) 136.884187 136.884193 136.884190
27:1) 136.884190 136.884193 136.884191
28:0) 136.884190 136.884191 136.884191
29:1) 136.884191 136.884191 136.884191
30:0) 136.884191 136.884191 136.884191

まずは緯度-180から180の範囲からスタートし、上の段のビット列をフラグとして、最初は1なのでその半分の0から180を選択します。次も1なので、0,180の中間90から180を選択します。このように30ビット分選択します。
経度については-90から90の範囲からスタートして、同様に選択していきます。

1:1) 0.000000 90.000000 45.000000
2:0) 0.000000 45.000000 22.500000
3:1) 22.500000 45.000000 33.750000
4:1) 33.750000 45.000000 39.375000
5:0) 33.750000 39.375000 36.562500
6:0) 33.750000 36.562500 35.156250
7:1) 35.156250 36.562500 35.859375
8:0) 35.156250 35.859375 35.507812
9:0) 35.156250 35.507812 35.332031
10:0) 35.156250 35.332031 35.244141
11:0) 35.156250 35.244141 35.200195
12:0) 35.156250 35.200195 35.178223
13:0) 35.156250 35.178223 35.167236
14:1) 35.167236 35.178223 35.172729
15:0) 35.167236 35.172729 35.169983
16:1) 35.169983 35.172729 35.171356
17:0) 35.169983 35.171356 35.170670
18:1) 35.170670 35.171356 35.171013
19:1) 35.171013 35.171356 35.171185
20:1) 35.171185 35.171356 35.171270
21:0) 35.171185 35.171270 35.171227
22:1) 35.171227 35.171270 35.171249
23:1) 35.171249 35.171270 35.171260
24:0) 35.171249 35.171260 35.171254
25:1) 35.171254 35.171260 35.171257
26:1) 35.171257 35.171260 35.171258
27:0) 35.171257 35.171258 35.171258
28:1) 35.171258 35.171258 35.171258
29:1) 35.171258 35.171258 35.171258
30:0) 35.171258 35.171258 35.171258

12文字だと精度が高いので、緯度経度どちらも30bitにたどりつくまでに有効桁数で変化がなくなりますが、精度が低いところでは地図上の範囲を表現していることが理解できます。
緯度経度からGeohashへの変換は、その座標がどの範囲なのかを判定してフラグをビットにしていく方法でできます。
このテストで、やはりビット操作はC言語がやりやすいとあたらめた思った次第です。

]]> Evaluate Code ../../../20150502327/ Sat, 02 May 2015 04:24:20 +0000 ../../../?p=327 コードを文字列データとして与え、プログラム中で評価して実行するしくみ(Eval Function)を、多くのスクリプト言語で持っています。Javascript,PHPなどでeval()という関数名で使われます。個人的にはBashやJavaScriptでよく使うのですが、ちょっとアグレッシブなことができたりします。
初めてこのようなことができると知ったのは、Lispがきっかけですが、まだWebもJavaもない時代で、プログラムを作りながら実行できるなんて、コンピュータがプログラムを自動的につくるなんて人工知能みたい、とショックを受けたのを覚えています。
プログラムコードとデータの境目がなくなるしくみは、たいへん興味深いです。
(XMLのXSLTも近いものを感じます)
Schemeで実際にどのように見えるか、ちょっと前にテストしたコードを見てみます。
環境 : DrRacket 6.1.1 / Windows 8.1
eval01
pで定義された処理を、後から変更(追加)しているところがポイントです。
式もデータも同じリストとして表現するLispならではの面白い部分です。

そこで、インタプリタでないコンパイラ言語のCでもeval()関数もどきができないか、やってみたらどうなるか、GWの自由研究みたいなのりでやってみました。

仕組みは、文字列で与えられたCコードを、ファイルに書き出し、これを外部コマンドでコンパイルしてダイナミックライブラリにします。これをランタイムで呼び出し実行します。

サンプルコードと、ビルドスクリプト(Pythonプログラム)をGithubにアップしました。
https://github.com/systemsblue/Eval-C-Function
ここでは、ビルドスクリプトとランタイムに生成されるファイルについて説明したいと思います。

環境 : Ubuntu 14.04
evalc_gen.h

int evalc(char *eval){
	void (*func)(__PSTRC *);
	void *so;
	__PSTRC ps;
	ps.p1 = p1;
	ps.p2 = p2;
	ps.pstr1 = pstr1;
	FILE *fp;

	fp = fopen("./sotemp.c", "w");
	fprintf(fp, __eval_fmt, eval);
	fclose(fp);
	system("cc -w -fPIC --share -o ./sotemp.so ./sotemp.c");

	so = dlopen("./sotemp.so", RTLD_LAZY);
	if(!so){
		fprintf(stderr, "%s\n", dlerror());
		return -1;
	}

	func = dlsym(so, "__feval");

	if(!func){
		fprintf(stderr, "%s\n", dlerror());
		return -2;
	}

	(*func)(&ps);
	dlclose(so);
	p1 = ps.p1;
	p2 = ps.p2;
	return 0;
}

ビルドスクリプトを実行すると、このヘッダーファイルが生成されます。
サンプルコードを解析して、evalc関数の最初と最後に、パラメータをI/O部分を記述しています。
サンプルソースの、
sample.c

int /*SO*/ p1;
int /*SO*/ p2;

char /*SO*/ pstr1[100];
char pstr2[100];

#include /*SO*/ "evalc_gen.h"

/*SO*/とコメントされている部分が処理対象となります。
evalc_gen.hは、生成ヘッダファイル名を指定します。生成された後読み込まれるもので、パラメータより後に記述します。

fprintf(fp, __eval_fmt, eval);

は、テンプレート__eval_fmtに対して、コードを書きこみます。

sotemp.c

#include<stdio.h>
#include<string.h>
typedef struct{
	int p1;
	int p2;
	char *pstr1;
} __PSTRC;
void __feval(__PSTRC *ps){
	int p1 = ps->p1;
	int p2 = ps->p2;
	char *pstr1; pstr1 = ps->pstr1;
	strcpy(pstr1, "AAAA");
	ps->p1 = p1;
	ps->p2 = p2;
}

ダイナミックライブラリの関数定義です。これはプログラム実行時に生成されます。evalc()の呼び出し分作成されます。(上書き)
sample.c

        // Eval code
        evalc("p1 /= 2; p2 -= 100");

        printf("p1 : %d  p2 : %d\n", p1, p2);

        // Eval code
        evalc("strcpy(pstr1, \"AAAA\");");

実行結果
eval02

制約条件としては、変数はグローバルで、型はポインタを使うのはcharのみです。
実用的かどうかはわかりませんが、自分ルールで作ってみるのは楽しいものです。

]]>