火曜日, 1月 21, 2014

Java8 - 関数の合成 1 .. andThen

Java8のラムダは関数の合成に対する操作も用意されている.
そのうちの一つがandThen()である.以下の例は引換券のIDを入力として本のタイトルを返す.


package org.tanuneko;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

public class AndThenSample {

    private static List<ClaimTicket> tickets;

    private static void init() {

        tickets = new ArrayList<ClaimTicket>();
        tickets.add( new ClaimTicket( 1, new Book( Janre.Sports, "All about Payton Manning") ) );
        tickets.add( new ClaimTicket( 2, new Book( Janre.Sports, "Andy - how to win at playoff") ) );
        tickets.add( new ClaimTicket( 3, new Book( Janre.Education, "ABC") ) );
        tickets.add( new ClaimTicket( 101, new Book( Janre.Education, "Waldorf education - how it works") ) );

    }

    public static void main( String args[] ) {

        init();
        Function<ClaimTicket,Book> getBookByClaimId = ClaimTicket::getBook;
        Function<Book,String> getTitle = Book::getTitle;
        Function<ClaimTicket,String> getTitleOfClaimedBook = getBookByClaimId.andThen(getTitle);
        System.out.println( tickets.stream().filter(x->x.getId()<100)
                .map(getTitleOfClaimedBook)
                .collect( Collectors.reducing((x,y)-> x + "," + y))
                .toString() );
    }

}

enum Janre {
    Education,
    Sports
}

class Book {

    private Janre janre;
    private String title;

    public Book( Janre janre, String title ) {
        this.janre = janre;
        this.title = title;
    }

    public Janre getJanre() { return janre; }
    public String getTitle() { return title; }
}

class ClaimTicket {

    private int id;
    private Book book;

    public ClaimTicket( final int id, final Book book ) {
        this.id = id;
        this.book = book;
    }

    public int getId() { return id; }
    public Book getBook() { return book; }
}

Java8 - Infiniteのstream

Stream.iterate()を使うとinfiniteのstreamを生成できる.infiniteのstream - ここではiterateの2番目の引数UnaryOperator<T> ( 引数Tを取り,処理後結果として入力と同型Tを返す.つまりFunction<T,T> )を無制限に適用しstreamの要素を生成する.

おおよその場合はStream.limit()と組み合わせて使う.
package org.tanuneko;

import java.util.stream.Stream;

public class ToMapSample {

    public static void main( String args[] ) {

        Stream.iterate( 'S', (x)->"ABC".charAt((int)(Math.random()*3)))
                .limit(30)
                .forEach(System.out::println);

    }

}

土曜日, 1月 18, 2014

Java8 - キーごとに分類

以下の例は本オブジェクトのリストに対し,各本のジャンルごとに分類した結果をマップにして返す例.IntelliJのエディタではシンタックスエラーが出るが,コンパイルも実行も問題ない.


package org.tanuneko;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static java.util.stream.Collectors.*;

public class ToMapSample {

    private static List<Book> books;

    private static void init() {

        books = new ArrayList<Book>();
        books.add( new Book( Janre.Sports, "All about Payton Manning") );
        books.add( new Book( Janre.Sports, "Andy - how to win at playoff")  );
        books.add( new Book( Janre.Education, "ABC") );
        books.add( new Book( Janre.Education, "Waldorf education - how it works")  );

    }

    public static void main( String args[] ) {

        init();
        Map<Janre,List<String>> map = books.stream()
                .collect(groupingBy(Book::getJanre,TreeMap::new,mapping(Book::getTitle,toList())));
        System.out.println(map);

    }

}

enum Janre {
    Education,
    Sports
}

class Book {

    private Janre janre;
    private String title;

    public Book( Janre janre, String title ) {
        this.janre = janre;
        this.title = title;
    }

    public Janre getJanre() { return janre; }
    public String getTitle() { return title; }
}




水曜日, 1月 15, 2014

Java8 - streamとDirectoryWalkerで効率よくファイル探索

Java IO CommonsのDirectoryWalkerとstreamを組み合わせて,効率よくファイルシステム上のデータを探すコード.かなり荒削りのサンプルだが,とりあえず動く.

package org.tanuneko.tanukin;

import org.apache.commons.io.DirectoryWalker;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Collectors;

public class DirWalker extends DirectoryWalker<File> {

    private static FileFilter filter = getFilter();

    public DirWalker() {

        super( filter,-1 );

    }

    protected void handleFile( File file, int depth, Collection<File> results ) {

        results.add( file );

    }

    public Collection<File> start( File startDir ) throws IOException {
        Collection<File> results = new ConcurrentLinkedQueue<File>();
        super.walk( startDir, results );
        return results;
    }

    public static void main(String args[] ) throws IOException {

        DirWalker walker = new DirWalker();
        System.out.println(
                        walker.start(new File("C:\\cygwin\\srv")).parallelStream()
                        .map(x -> x.getAbsolutePath())
                        .collect(Collectors.toStringJoiner("\n"))
                        .toString());

    }

    // if you want to handle directory, define below
    /*
    protected boolean handleDirectory( File dir, int depth, Collection<File> results ) {

        if( ".svn".equals( dir.getName() ) ) {
            return false;
        }
        else {
            return true;
        }
    }
    */

    private static FileFilter getFilter() {

        //    you can define filter
        //    IOFileFilter suffixFilt =  FileFilterUtils.suffixFileFilter( ".txt" );
        //    FileFilterUtils.makeSVNAware( suffixFilt );
        //   FileFilter filter = FileFilterUtils.or( suffixFilt );
        //    return filter;
        return null;

    }

}

日曜日, 1月 12, 2014

Java8 - オブジェクトへのmapper適用

以下の例はJava8のmapを用いて,

  1. カスタムオブジェクトItemのマスタリスト中から興味のある品だけを取り出し
  2. 更に10000円以上のものだけを選び
  3. かつ価格を安いものから昇順に並べる

例である.


package org.tanuneko;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Reducer {

    public static void main( String args[] ) {

        // since this is lazy evaluation, WareHouse::findItembyId is not called for all entries in item list held by WareHouse
        List<Integer> interests = Arrays.asList( 1,2,3,4,5,6 );
        List<Item> items = interests.stream().map(WareHouse::findItembyId)
                            .filter((x)->x.getPrice()>10000)
                            .sorted( (x,y)->x.getPrice() - y.getPrice() )
                            .collect(Collectors.toList());
        items.stream().forEach(System.out::println);
 
     }

}

class WareHouse {

    static final List<Item> items = genItems();

    public static Item findItembyId( int id )  {
        // better to use orElse()
        return items.stream().filter(x->x.getId()==id).findFirst().get();

    }

    private static List<Item> genItems() {
        List<Item> items = new ArrayList<>();
        items.add( new Item( 1, 500, "Basket" ) );
        items.add( new Item( 2,1200, "Rack" ) );
        items.add( new Item( 3, 700, "Small Table" ) );
        items.add( new Item( 4, 50000, "Granite Table" ) );
        items.add( new Item( 5, 15000, "Closet" ) );
        items.add( new Item( 6, 400, "Grout" ) );
        items.add( new Item( 7, 28000, "Small Couch" ) );
        return items;
    }
}

Items.java

package org.tanuneko;

public class Item {

    private int id;
    private int price;
    private String name;

    public Item( int id, int price, String name ) {
        this.id = id;
        this.price = price;
        this.name = name;
    }

    public int getId() { return id; }
    public int getPrice() { return price; }
    public String getName() { return name; }

    @Override
    public String toString() {
        return "Item{" +
                "id=" + id +
                ", price=" + price +
                ", name='" + name + '\'' +
                '}';
    }
}

Item{id=5, price=15000, name='Closet'}
Item{id=4, price=50000, name='Granite Table'}

土曜日, 1月 11, 2014

Java8 - reduceとmapで集計

ついでにmapでカスタムオブジェクト群のあるフィールド値の合計をmapとreduceで行う例.

package org.tanuneko;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BinaryOperator;

public class Reducer {

    public static void main( String args[] ) {

        System.out.println( genItems().stream().map( Item::getPrice ).reduce( Integer::sum ).get() );
        
     }

    private static List<Item> genItems() {
        List<Item> items = new ArrayList<>();
        items.add( new Item( 500, "Basket" ) );
        items.add( new Item( 1200, "Rack" ) );
        items.add( new Item( 700, "Small Table" ) );
        items.add( new Item( 50000, "Granite Table" ) );
        items.add( new Item( 15000, "Closet" ) );
        items.add( new Item( 400, "Grout" ) );
        return items;
    }

    private static Item getCheapestOne() {
        return new Item( Integer.MIN_VALUE, "VOID" );
    }
}

package org.tanuneko;

public class Item {
    private int price;
    private String name;

    public Item( int price, String name ) {
        this.price = price;
        this.name = name;
    }

    public int getPrice() { return price; }
    public String getName() { return name; }

    @Override
    public String toString() {
        return "Item{" +
                "price=" + price +
                ", name='" + name + '\'' +
                '}';
    }
}


Java8 - reduceで検索

Java8のstreamのreduceを使ってカスタムオブジェクト(この例ではItem)の検索をする例.


Item.java
package org.tanuneko;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BinaryOperator;

public class Reducer {

    public static void main( String args[] ) {

        BinaryOperator<Item> costsMore = (m,n)->
                                          { return ( m.getPrice() > n.getPrice() ? m:n ); };

        System.out.println(genItems().stream().reduce(getCheapestOne(), costsMore));
     }

    private static List<Item> genItems() {
        List<Item> items = new ArrayList<>();
        items.add( new Item( 500, "Basket" ) );
        items.add( new Item( 1200, "Rack" ) );
        items.add( new Item( 700, "Small Table" ) );
        items.add( new Item( 50000, "Granite Table" ) );
        items.add( new Item( 15000, "Closet" ) );
        items.add( new Item( 400, "Grout" ) );
        return items;
    }

    private static Item getCheapestOne() {
        return new Item( Integer.MIN_VALUE, "VOID" );
    }
}

Item.java

package org.tanuneko;

public class Item {
    private int price;
    private String name;

    public Item( int price, String name ) {
        this.price = price;
        this.name = name;
    }

    public int getPrice() { return price; }
    public String getName() { return name; }

    @Override
    public String toString() {
        return "Item{" +
                "price=" + price +
                ", name='" + name + '\'' +
                '}';
    }
}
実行結果
Item{price=50000, name='Granite Table'}

火曜日, 12月 31, 2013

Java8 - stream

Java8の変更点の目玉の一つがstreamである.今までのfor文よりもずっときれいにデータプロセス用のコードが書けるようになる.

以下の例は,とある場外取引システムにおけるトレードデータから以下の条件を満たすものを抽出した例.

  • TradeDateが2013年1月1日以降
  • アクション名がADD
  • 取引相手先国が日本

package org.tanuneko.sample;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;

public class StreamSample {

    private static List<Trade> trades = populateTrades();
    private static List<CounterParty> cps = populateCPs();

    private static List<Trade> populateTrades() {
        List<Trade> list = new ArrayList<Trade>();
        list.add( new Trade( "TRD111.1", 1, "ADD", parse( "2013-01-20" ), 1001, true, "15000000ABC" ) );
        list.add( new Trade( "TRD123.1", 1, "ADD", parse( "2013-01-20" ), 1006, true, "16000000ABC" ) );
        list.add( new Trade( "TRD123.1", 2, "MOD", parse( "2013-01-26" ), 1006, true, "16000000ABC" ) );
        list.add( new Trade( "TRD145.1", 1, "ADD", parse( "2013-09-20" ), 1009, true, "T7000000ABC" ) );
        list.add( new Trade( "TRD133.1", 1, "ADD", parse( "2013-06-20" ), 1001, true, "16600000ABC" ) );
        list.add( new Trade( "TRD080.1", 1, "ADD", parse( "2010-07-20" ), 1001, true, "TRD080.1" ) );
        list.add( new Trade( "TRD092.1", 1, "ADD", parse( "2010-09-15" ), 1008, true, "TRD092.1" ) );
        list.add( new Trade( "TRD092.1", 2, "MOD", parse( "2010-09-30" ), 1020, true, "TRD092.1" ) );
        return list;
    }

    private static List<CounterParty> populateCPs() {
        List<CounterParty> cps = new ArrayList<CounterParty>();
        cps.add( new CounterParty( 1001, "US" ) );
        cps.add( new CounterParty( 1009, "Ireland" ) );
        cps.add( new CounterParty( 1006, "Japan" ) );
        cps.add( new CounterParty( 1020, "India" ) );
        cps.add( new CounterParty( 1008, "France" ) );
        cps.sort((l,r) -> l.getCounterPartyId() - r.getCounterPartyId()  );
        return cps;
    }

    private static ZonedDateTime parse( String date ) {
        return ZonedDateTime.parse( date + "T00:00:00-05:00[America/New_York]" );
    }

    public static void main( String args[] ) {
        
        trades.stream()
                .filter(t -> t.getTradeDate().isAfter(parse("2013-01-01")))
                .filter(t -> t.getActionName().equals("ADD") )
                .filter(t -> cps.stream().anyMatch( x -> x.getCounterPartyId() == t.getCounterPartyId() && x.getCountry().equals( "Japan" ) ) )
                .forEach(t -> System.out.println(t));
        cps.forEach( c -> System.out.println( c ) );
    }
}

class CounterParty {

    private int counterPartyId;
    private String country;

    CounterParty(int counterPartyId, String country) {
        this.counterPartyId = counterPartyId;
        this.country = country;
    }

    public int getCounterPartyId() {
        return counterPartyId;
    }

    public String getCountry() {
        return country;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        CounterParty that = (CounterParty) o;

        if (counterPartyId != that.counterPartyId) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return counterPartyId;
    }

    @Override
    public String toString() {
        return "CounterParty{" +
                "counterPartyId=" + counterPartyId +
                ", country='" + country + '\'' +
                '}';
    }
}

class Trade {

    private String tradeId;
    private int eventId;
    private String actionName;
    private ZonedDateTime tradeDate;
    private int counterPartyId;
    private boolean isSDREligible;
    private String USI;

    public Trade( String tradeId, int eventId, String actionName, ZonedDateTime tradeDate,
                   int counterPartyId, boolean isSDREligible, String USI ) {
        this.tradeId = tradeId;
        this.eventId = eventId;
        this.actionName = actionName;
        this.tradeDate = tradeDate;
        this.counterPartyId = counterPartyId;
        this.isSDREligible = isSDREligible;
        this.USI = USI;
    }

    public String getTradeId() {
        return tradeId;
    }

    public int getEventId() {
        return eventId;
    }

    public String getActionName() {
        return actionName;
    }

    public ZonedDateTime getTradeDate() {
        return tradeDate;
    }

    public int getCounterPartyId() {
        return counterPartyId;
    }

    public boolean isSDREligible() {
        return isSDREligible;
    }

    public String getUSI() {
        return USI;
    }

    @Override
    public String toString() {
        return "Trade{" +
                "tradeId='" + tradeId + '\'' +
                ", eventId=" + eventId +
                ", actionName='" + actionName + '\'' +
                ", tradeDate=" + tradeDate +
                ", counterPartyId=" + counterPartyId +
                ", isSDREligible=" + isSDREligible +
                ", USI='" + USI + '\'' +
                '}';
    }
}



月曜日, 12月 30, 2013

Java8 新しい Date and Time API

Java8の新機能の一つが新しいDate and Time API(JSR-310)の追加だろう.ご存じの通り標準SDKに付属していた従来のDate関連のクラスは機能が貧弱であったり,Calendarクラスmutabilityに起因する懸念から,Joda Timeを使っていた.
この新しいDate and Time API(JSR-310)を使うか,Joda Timeを使い続けるかはあなた次第だが,少なくとも旧来のDateの実装で無理をし続ける必要だけはなさそうである.

ちなみに以下のサイトは良質なDate and Time APIのサマリ,Joda TimeとJSR-310の比較記事だと思う.

http://sssslide.com/www.slideshare.net/JustinSDK/2013-java-developer-day-joda-timejsr310
http://howtodoinjava.com/2013/05/15/date-and-time-api-changes-in-java-8-lambda/

LocalDate, ZoneDateTimeそしてDateTimeFormatterの使用例

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;

/**
 * Created by morinoko on 12/29/13.
 */
public class Kerorin {

    public static void main( String args[] ) {

        LocalDate date = LocalDate.now();
        System.out.println( "today:" + date );
        System.out.println( "after five days from today:" + date.plusDays(5) );
        //ZoneId.getAvailableZoneIds().forEach(x -> System.out.println(x));
        ZonedDateTime zTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
        DateTimeFormatter fmt = new DateTimeFormatterBuilder().append( DateTimeFormatter.ISO_DATE )
                .appendLiteral("-")
                .appendZoneId().toFormatter();

        System.out.println( fmt.format( zTime ) );
        System.out.println( "Converting to JST" );
        System.out.println( fmt.format( zTime.withZoneSameLocal( ZoneId.of( "Asia/Tokyo" ) ) ) );
      }

}



Java8 ラムダ

関数言語の使用者には十二分におなじみのlambda式がついにJava バージョン8で正式採用である.今まで無名クラスを書いてはコードが汚れるたびに,関数ポインタのように書ければなぁ,と思っていたのだが..これからはLambda式で綺麗にかけるようになるわけである.実にありがたい.

Lambda式を使ってリストをソート後ダンプ

import java.util.Arrays;
import java.util.List;

/**
 * Created by morinoko on 12/29/13.
 */
public class Tanukin {

    public static void main( String args[] ) {

        List<String> list = Arrays.asList( new String[]{"tanu", "tanuku", "hello"} );
        list.sort( (l,r) -> l.length() - r.length()  );
        list.forEach( s -> System.out.println( s ) );

    }

}
C++での例
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;

bool cmp(const string& l, const string& r) { return l.size() < r.size(); }
void dump(const string& s){ cout << s << endl; }

int main() {

 vector<string> v;
 v.push_back( "tanu" ); v.push_back( "tanuku" ); v.push_back( "hello" );
 sort( v.begin(), v.end(), cmp );
 for_each( v.begin(), v.end(), dump );
 
}


土曜日, 11月 16, 2013

C++ STL - Partition関数

STL algorithmのpartition関数は特定の要素をリスト中で仕分けできる便利な関数なので覚えておくとよいことが色々あるかも.以下の例はファイル名のリスト中jpegファイル名の要素とそうでない要素を分類する例.

#include <string>
#include <algorithm>
#include <vector>
#include <iostream>
#include <sstream>
using namespace std;

static vector<string> JPEG_POSTFIX;

void init() {

 JPEG_POSTFIX.push_back(".jpg");
 JPEG_POSTFIX.push_back(".jpeg");

}

bool IsJpeg(const string& s) {
        // should use boost's to_lower, though!
 vector<string>::iterator iter;
 for(iter = JPEG_POSTFIX.begin();iter != JPEG_POSTFIX.end();iter++ ) {
  if(s.find(*iter) != string::npos)
   return true;
 }
 return false;

}

int main() {

 init();
 istringstream f("neko.jpg tako.jpeg ika.png rakko.gif fugu.jpg");
 vector<string> fileList;
 string s;
 while(getline(f,s,' ')) {
  fileList.push_back(s);
 }
 cout << "## before partitioning.." << is_partitioned( fileList.begin(),fileList.end(),IsJpeg ) << endl;
 for_each(fileList.begin(),fileList.end(),[](string& s){cout << s << " "; });
 partition(fileList.begin(),fileList.end(),IsJpeg);
 cout << endl << "## after partitioning.." << is_partitioned( fileList.begin(),fileList.end(),IsJpeg ) << endl;
 for_each(fileList.begin(),fileList.end(),[](string& s){cout << s << " "; });
 cout << endl;

 return 0;

}


月曜日, 11月 11, 2013

Java - StrSubstitutor

Apache Commons langからまた一つ便利なクラスのご紹介をさせていただきたい.
StrSubstitutorクラスは名前の通り文字列中のキーワード変換を行う.キーワードのプレフィックス・ポストフィックスの変更並びに変換後の値中のキーワードに対しての変換もサポートしている優れものである.この車輪の再発明はしないのが賢明だろう.

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.text.StrSubstitutor;
import org.junit.Test;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.*;

public class StrSubstTest {

 @Test
 public void testSubst() {
  
  final String TEST_STR = "${PATTERN} cat";
  final String EXPECTED_STR = "black cat";
  
  StrSubstitutor subst = new StrSubstitutor( prepareValueMap() );
  assertThat( subst.replace( TEST_STR ), is( EXPECTED_STR ) );
  
 }
 
 @Test
 public void testSubstWithPrefix() {
  
  final String TEST_STR = "<<PATTERN>> cat";
  final String EXPECTED_STR = "black cat";
  
  StrSubstitutor subst = new StrSubstitutor( prepareValueMap(), "<<", ">>" );
  assertThat( subst.replace( TEST_STR ), is( EXPECTED_STR ) );
  
 }
 
 @Test
 public void testSubstInVariable() {
  
  final String TEST_STR = "${PATTERN} cat";
  final String EXPECTED_STR = "calico(white,black,brown) cat";
  
  StrSubstitutor subst = new StrSubstitutor( prepareValueMap2() );
  subst.setEnableSubstitutionInVariables( true );
  assertThat( subst.replace( TEST_STR ), is( EXPECTED_STR ) );
  
 }
 
 private Map<String,String> prepareValueMap() {
  
  Map<String,String> map = new HashMap<String,String>();
  map.put( "PATTERN", "black" );
  map.put( "SIZE", "big" );
  return map;
  
 }
 
 private Map<String,String> prepareValueMap2() {
  
  Map<String,String> map = new HashMap<String,String>();
  map.put( "PATTERN", "calico(${COLORS})" );
  map.put( "COLORS", "white,black,brown" );
  return map;
  
 }

}

水曜日, 10月 09, 2013

all of, any of, none of

リスト・配列要素検査の関数all_of,any_of,none_ofは是非覚えておくとよい.

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

bool chk(string s) {
 return s.size() > 5;
}

int main() {

 vector<string> v;
 v.push_back("Warsaw");
 v.push_back("NewYork");

 // all of
 cout << ( all_of(v.begin(),v.end(),chk) ? "[GOOD] all good" : "some failed or all failed" ) << endl;
 v.push_back("Tokyo");
 cout << ( all_of(v.begin(),v.end(),chk) ? "[GOOD] all good" : "some failed or all failed" ) << endl;

 v.clear();
 v.push_back("Rome");
 v.push_back("Nagoya");

 // any of
 cout << ( any_of(v.begin(),v.end(),chk) ? "[GOOD] some good" : "all failed" ) << endl;
 v.erase( find( v.begin(),v.end(),"Nagoya"));
 v.push_back("Osaka");
 cout << ( any_of(v.begin(),v.end(),chk) ? "[GOOD] some good" : "all failed" ) << endl;

 // none of
 cout << ( none_of(v.begin(),v.end(),chk) ? "[GOOD] all failed" : "not all failed" ) << endl;
 v.erase(find(v.begin(),v.end(),"Osaka"));
 v.push_back("Mangalore");
 cout << ( none_of(v.begin(),v.end(),chk) ? "[GOOD] all failed" : "not all failed" ) << endl;

 return 0;

}


C++でforeach

ForeachはJavaやPythonといった他の言語で多用されている反復処理用構文である.
C++ではSTLのalgorithmがそれを提供してくれている.是非使おう.

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>

using namespace std;

void show(const string s) {
 cout << s << ' ';
}

int main() {

 vector<string> v;
 v.push_back("Algonquin");
 v.push_back("Iroquois");

 for_each(v.begin(),v.end(),show);

 return 0;

}


lvalueとrvalue

C++のコンテクストでたまに見かける用語の一つがlvalueとrvalueである.
これを知らないと苦労することもあるかもしれないので是非覚えておいてほしい.

lvalue .. いわゆる変数・定数.2回以上使いまわされることもあるかもしれないデータである.
rvalue .. いわゆる代入される又は出現後即揮発するデータ.

int i = 100 * 3;

とあればiがlvalue, 100 * 3 がrvalueである.

インライン関数

Q. classの宣言中に関数を定義した場合,その関数はインライン扱いされるか?

A. yes



土曜日, 8月 03, 2013

プチクイズ8 - コピーコンストラクタ

Q. 以下のコードのコンパイル&実行結果は?

#include <cstdio>
#include <cstdlib>
#include <string>

class C {
private:
 int d;
public:
 C() { d = 5;};
 ~C() {};
 C(C m) { d = m.d; }
 virtual void a() { printf("aaa"); }
 int GetD() { return d; }
};

int main() {

 C m;
 C m2(m);
 
 printf("%d\n",m2.GetD());
}

A.
大抵のコンパイラではコンパイルできないはず.上記コピーコンストラクタは値渡しのパラメータが定義されているので,このコピーコンストラクタが呼ばれ,ランタイム上で値渡しのパラメータmを”コピー”しようとコピーコンストラクタを更に呼び... と永遠に再帰呼び出しが発生する.
この問題を避けるには,const C&にパラメータの宣言を変更すればよい.

プチクイズ 7 - クラスのsizeof

Q1. 空のクラスに対してのsizeofの結果は?

A1. (visual studioでもgnu c++でも)1.実質データは0だがクラスの存在を示すためにダミーのデータ(size 1)が作られる.

Q2. コンストラクタとデストラクタを宣言した場合?

A2. 変わらず.ただしvirtualであればvtableを作るのでその分のサイズは増える.32bitマシン上なら4, 64bitマシン上なら8.

Q3. 関数を宣言すると?

A3. コンストラクタ・デストラクタに同じ.virtualであればvtable分の増加,さもなければ何も変わらず.

Q4. intの変数をクラスに追加すると?

A4. int型分のサイズが増加.

アルゴリズム問題を解く 13 - 回文

問題

Please implement a function that checks whether a positive number is a palindrome or not.
For example, 121 is a palindrome, but 123 is not. Please write an answer in C.
( Coding interviewsより抜粋 )

ヒント

山本山,とかたけやぶやけた,に共通するのは,文字列長が奇数であることである.この問題はマイナスでない整数であることも忘れずに.

答え1

先頭と最後尾,先頭+1と最後尾-1..を比較していけばよいのでO(N/2)の計算量でよい.
NULLの場合,負の場合等々といったチェックも忘れずに.

#include <cstdio>
#include <cstdlib>
#include <string>

int isdigits(const char* const s, int len) {

 int result = 1;
 for( int i = 0;i < len;i++ ) {
  if( !isdigit(s[i]) ) {
   result = 0;
   break;
  }
 }

 return result;

}

int ispalindrome(const char* const s, unsigned int len) {

 // prerequisite check
 if( s[0] == '-' ||
  !(len % 2) ||
  len == 1 ||
  s == NULL ||
  !isdigits(s,len))
  return 0;
  
 int result = 1;
 unsigned int half = len >> 1;
 for( unsigned int i = 0;i < half;i++ ) {
  if(s[i] != s[len-1-i]) {
   result = 0;
   break;
  }
 }
 return result;
}

int main() {

 char* s1 = "142";
 char* s2 = "5";
 char* s3 = "282";
 char* s4 = "2820";
 char* s5 = "-10";
 char* s6 = "ABA";
 printf("%d\n",ispalindrome(s1,strlen(s1)));
 printf("%d\n",ispalindrome(s2,strlen(s2)));
 printf("%d\n",ispalindrome(s3,strlen(s3)));
 printf("%d\n",ispalindrome(s4,strlen(s4)));
 printf("%d\n",ispalindrome(s5,strlen(s5)));
 printf("%d\n",ispalindrome(s6,strlen(s6)));

 char ch;
 scanf(&ch);
}

答え2

答え1よりはるかに優れたコードである.

#include <cstdio>
#include <cstdlib>
#include <string>

int ispalindrome(unsigned int n) {

 if( n <= 110 )
  return 0;

 unsigned int orig = n;
 unsigned int rev = 0;
 while( n != 0 ) {
  rev = rev * 10 + n % 10;
  n /= 10;
 }

 return (rev == orig) ? 1 : 0;

}

int main() {

 unsigned int s1 = 142;
 unsigned int s2 = 5;
 unsigned int s3 = 282;
 unsigned int s4 = 2820;
 unsigned int s5 = 111;

 printf("%d\n",ispalindrome(s1));
 printf("%d\n",ispalindrome(s2));
 printf("%d\n",ispalindrome(s3));
 printf("%d\n",ispalindrome(s4));
 printf("%d\n",ispalindrome(s5));

}

金曜日, 8月 02, 2013

constの使い方

面接をしていて感じたのが,意外にconstの使い方をわかっていない又は誤って理解しているケースも多いということである.

constの正しい用法はcode as documentationとして重要なのでしっかり理解しておきたいところ.

int i = 500;
// 1. 
const int* pVal = &i;
// 2.
int* const pVal = &i;
// 3.
const int* const pVal = &i;

1はconst修飾子が適用されたint型,つまり定数intを指すポインタである.
ゆえにポインタを経由してpValの値を変更することはコンパイラが許可しないが,pVal自体別のconst int型を指し示すことはできる.

2はint型を示すconst修飾子が適用されたポインタ,言い換えるとそのポインタの持つ値,つまりはアドレスの変更が不可能なポインタである.ポインタ経由でiの値を変更することは認めるが,i意外の変数を指し示すことは許さない.

3は定数int型を示す定数ポインタである.ポインタ経由での値の変更も許されなければ,またポインタの指す値を変えることも出来ない.