Monday, March 7, 2016
Ինչի՞ց սկսել Java լեզվի ուսումնասիրությունը
1. Մի քանի տարի առաջ եմ հայտնաբերել Robert Sedgewick-ի, Kevin Wayne֊ի «Introduction to programming in Java», գիրքը, որը նախատեսված է Պրինստոնի համալսարանում ծրագրավորման սկզբնական դասընթացի համար։ Գեղեցիկ ու բազմազան օրինակներով, սխեմաներով ու պատկերներով հարուստ գիրք է։ (Վերջերս այս գիրքը վերահրատարակվել է՝ Java լեզուն Python լեզվով փոխարինված։) 2. Java 8. Руководство для начинающих, Герберт Шилдт ― հաջող գիրք է, հայտնի հեղինակը բավականին մանրամասնորեն շարադրում է Ջավա լեզվի վերջին տարբերակի հնարավորությունները։ Շատ հարմար է սկսելու համար։ 3. Java 8. Полное руководство, Герберт Шилдт ― նույն հեղինակի մի ուրիշ գիրք, որն արդեն Ջավա լեզվի սպառիչ տեղեկատու է։ Այս գրքում կարելի է գտնել համարյա ամեն ինչ։ 4. Java 7. Наиболее полное руководство, Хабибуллин Ильдар ― էլի լավ գիրք է, բայց սա ես խորհուրդ կտայի պարզապես ձեռքի տակ ունենալ, և եթե ին֊որ բան չես գտնում Շիլդտի գրքերում, նայել այստեղ։ 5. Язык программирования Java SE 8. Подробное описание, Джеймс Гослинг, Гай Л. Стил և ուրիշներ ― հզոր գիրք է։ Հեղինակները հենց Ջավա լեզվի հեղինակներն են, ովքեր Ջավայի մասին գիտեն ամեն ինչ։ 6. Java. Эффективное программирование, Блох Джошуа ― նորից հետաքրքիր գիրք է, որտեղ քննարկվում են Ջավա լեզվով ծրագրավորման առանձին հարցեր, առաջարկվում են հաճախ հանդիպող խնդիրների արդյունավետ լուծումներ։ 7. Алгоритмы на Java, Седжвик Роберт, Уэйн Кевин ― իմ ամենասիրած գրքերից է։ Սա արդեն ոչ թե Ջավա լեզվի մասին է, այլ Ալգորիթմների իրականացման մասին է Ջավա լեզվի օգտագործմամբ։ 8. Algorithms (4th Edition), Robert Sedgewick, Kevin Wayne ― նախորդ գրքի անգլերեն (օրիգինալ) տարբերակն է։
Բոլոր գրքերը կարելի է գտնել այստեղ։
Saturday, June 6, 2015
March թեսթի իրականացումը Java լեզվով
պատճառով։ Ցանկացողները կարող են շարունակել այն։
Կարծում եմ, որ, զարգացնելու դեպքում, կարող է լավ
կուրսային աշխատանք լինել։
Հիշող սարքերի ներդրված թեսթավորման տարածված եղանակներից մեկը March թեսթերի կիրառումն է։ Այս գրառման մեջ ես ուզում եմ պատմել հիշող սարքի մոդելի, անսարքության մոդելի, ինչպես նաև March թեսթի մոդելի իրականացման մասին։
Ընդհանուր առմամբ գաղափարը հետևյալն է․ a) սահմանել տրված չափերով հիշող սարք, b) նրանում ներմուծել որոշ տիպի անսարքություններ, c) կազմել March թեսթեր, d) գործարկել թեսթերը հիշող սարքի մոդելի վրա և գրանցել արդյունքները։
Նշված տիպի մոդելավորումն օգտագործվում է March թեսթերի մշակման համար։ Մոդելավորման օգնությամբ ստեղծվում են թեսթեր, որոնք պետք է հիշող սարքի վրա հայտնաբերեն թեսթավորողին հայտնի անսարքությունները։
Հիշող սարքը
Սովորական և անսարք բջջի մոդելը
Հիշող սարքը մեկ բիթ ինֆորմացիա պահող հիշող տարրերի՝ բջիջների մատրից է։ Այդ բջջի մոդելը ես իրականացրել եմCell դասով։ Այս Cell-ը սարքին բջջի մոդելն է։
package memory;
/**
* Հիշող բջջի մոդելը
*/
class Cell {
// տվյալ բջիջը պարունակող մատրիցը
private Memory parent = null;
// տողը և սյունը
private int row = 0, column = 0;
// բջջի արժեքը
private char value = 'x';
// կոնստրուկտորը
public Cell(Memory p, int r, int c)
{
parent = p;
row = r;
column = c;
}
// վերադարձնում է արժեքը
public char read()
{
return value;
}
// փոխում է արժեքը
public void write(char v)
{
value = v;
}
}
Այս դասի parent անդամը այն հիշող սարքի մոդելի հղում է, որը պարունակում է տվյալ բջիջը։ row և column ինդեքսները ցույց են տալիս, թե հիշող սարքի մատրիցի որ տողում և սյունում է տեղադրված բջիջը։ Այս երեք հատկությունները հնարավորություն են տալիս մոդելավորել այնպիսի անսարքություններ, որտեղ մի բջջի ոչ սարքին լինելը ազդում է մեկ այլ սարքին բջջի պարունակության վրա։Հիշող բջջի հետ կապված անսարքությունները մոդելավորելու համար պետք է ընդլայնել
Cell դասը։ Օրինակ, հաճախ են հանդիպում այնպիսի բջջիջներ, որոնց կարդալու գործողությունը վերադարձնում է հաստատուն 0 կամ 1 արժեքը։ Հաստատուն զրո վերադարձնող բջիջը կարելի է մոդելավորել, օրինակ, ConstZero դասով։
package memory;
/**
* Մշտական '0' վերադարձնող անսարք բջիջ
*/
public class ConstZero extends Cell {
public ConstZero(Memory p, int r, int c)
{
super(p, r, c);
}
@Override
public char read()
{
return '0';
}
}
Բջիջների մատրիցը
Հիշող սարքը, որը հիշող բջիջների մատրից է մոդելավորված էMemory դասով։ Այն պարունակում է տողերի ու սյուների քանակները ցույց տվող rows և columns անդամները, և Cell օբյեկտների matrix մատրիցը։
package memory;
/**
* Հիշողղ սարքի մոդելը
*/
public class Memory {
// տողերի քանակը
public int rows = 0;
// սյուների քանակը
public int columns = 0;
// բջիջների մատրիցը
private Cell[][] matrix = null;
// կոնստրուկտորը
public Memory(int r, int c)
{
rows = r;
columns = c;
matrix = new Cell[rows][columns];
for( int i = 0; i < rows; ++i )
for( int j = 0; j < columns; ++j )
matrix[i][j] = new Cell(this, i, j);
}
// անսարքություն մոդելավորելու համար
// տրված դիրքում տեղադրել տրված բջիջը
public void replaceCell(int r, int c, cell cl)
{
matrix[r][c] = cl;
}
// կարդալ, տրված է գծային հասցեն
public char read(int addr)
{
return read(addr / rows, addr % rows);
}
// կարդալ, տրված է ֆիզիկական հասցեն
private char read(int r, int c)
{
return matrix[r][c].read();
}
// գրել, տրված է գծային հասցեն
public void write(int addr, char d)
{
write(addr / rows, addr % rows, d);
}
// գրել, տրված է ֆիզիկական հասցեն
private void write(int r, int c, char d)
{
matrix[r][c].write(d);
}
// արտածել մատրիցի պարունակությունը
public void Print()
{
for( int r = 0; r < rows; ++r ) {
System.out.printf("%x: ");
for( int c = 0; c < columns; ++c )
System.out.print(read(r,c));
System.out.println();
}
System.out.println();
}
}
Կոնստրուկտորում matrix մատրիցը արժեքավորվում է «սարքին» բջիջներով։ Իսկ replaceCell մեթոդը հնարավորություն է տալիս մատրիցի տրված դիրքի բջիջը փոխարինել նոր տրվածով։ replaceCell մեթոդին տրվում են բջջի ֆիզիկական հասցեները՝ տողը և սյունը, քանի որ հաճախ հիշող սարքերում կիրառվում է ավելի բարդ հասցեավորում քան գծայինն է, և հարմար է անսարք բջիջը տեղադրել ըստ ֆիզիկական դիրքի։Հիմա, օրինակ, կարող եմ ստեղծել
8 տողեր և 4 սյուներ ունեցող հիշող սարք և դրա երկրորդ տողի առաջին բջջում տեղադրել մի անսարք բջիջ։
Memory mem = new Memory(8, 4); mem.replaceCell(1, 0, new ConstZero(mem, 1, 0));Կամ, քանի որ Java լեզուն հնարավորություն է տալիս օբյեկտի ստեղծման ժամանակ վերասահմանել նրա մեթոդները, կարող եմ
ConsOne տիպի անսարքություն մոդելավորել այսպես․
mem.replaceCell(1, 1, new Cell(mem, 1, 1) {
@Override
public byte read() { return '1'; }
});
March թեսթը
March թեսթը March ալգորիթմների շարք է, որոնք հաջորդաբար գործարկվում են հիշող սարքի մոդելի վրա և ստեղծում են թեսթավորման արդյունքներ։ Ալգորիթմն իր հերթին March էլեմենտների շարք է, իսկ այս վերջինն էլ March գործողությունների շարք է։Ես դիտարկում եմ միայն պարզագույն March թեսթերը, որոնցում հանդիպում են միայն գրելու և կարդալու գործողություններ։ Եվ որպեսզի March թեսթը նկարագրելիս ավելի անսխալ լինեմ, բերեմ դրա EBNF քերականությունը։
MarchTest = { Algorithm }.
Algorithm = IDENT '{' Element { ';' Element } '}'.
Element = ('=>' | '<=') '(' Operation { ',' Operation } ')'.
Operation = ('W' | 'R') ('0' | '1').
March գործողություն
Տվյալ կոնտեքստում կան երկու March գործողություններ․ բջջում տրված արժեքը գրողW (write) գործողությունը, և բջջից տրված սպասվող արժեքը կարդացող R (read) գործողությունը։Ես ուզում եմ նշված երկու գործողությունները մոդելավարել
Operation դասով։ Վերջինիս code անդամը գործողության տեսակն է՝ R կամ W, իսկ data անդամը՝ այն արժեքը, որը պետք է գրել բջջում, կամ պետք է սպասել բջջից կարդալիս։
package march;
import memory.*;
public class Operation {
// գործողությունը
private char code = 'X';
// գրելու կամ կարդալու տվյալը
private char data = '?';
public Operation(char c, byte d)
{
code = c;
data = d;
}
March գործողությունը Memory օբյեկտի վրա գործարկելու համար սահմանեմ runOn մեթոդը, որը ստանում է թեսթավորվող հիշող սարքի հղումը և այն գծային հասցեն, որում գտնվող բջջի նկատմամբ կիրառվում է գործողությունը։
public boolean runOn(Memory mem, int addr)
{
boolean status = true;
if( opcode == 'W' )
mem.write(addr, value);
else if( opcode == 'R' )
status = value == mem.read(addr);
if( !status )
System.out.printf("'%s' գործողությունը ձախողվեց %d հասցեի վրա։",
toString(), addr);
return status;
}
Եվ վերջապես, ToString մեթոդը, որը վերադարձնում է գործողության տեքստային տեսքը։
@Override
public String toString()
{
return Character.toString(opcode) + Character.toString(value);
}
}
March էլեմենտ
March էլեմենտն ունի երկու բաղադրիչ․ a) հասցեների թվարկման ուղղությունը՝ որոշվող=> և <= սիմվոլներով, և b) March գործողությունների շղթան։
package march;
import memory.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* March էլէմենտը
*/
public class Element {
// հասցեները նվազում են
public static final int DEC = -1;
// հասցեներն աճում են
public static final int INC = 1;
// հասցեի թվարկման ուղղությունը
private int direction = 0;
// գործողությունների շարքը
private List<Operation> operations = null;
public Element(int dir, List<Operation> opers)
{
direction = dir;
operations = new ArrayList<Operation>();
operations.addAll(opers);
}
March էլեմենտի գործարկումը հիշող սարքի մոդելի վրա կատարվում է runOn մեթոդով։ Քանի որ հասցեների թվարկման ուղղությունը կարող է լինել ինչպես աճող այնպես էլ նվազող, ապա begin և end փոփոխականներում հաշվում եմ առաջին ու վերջին հասցեն։ Հետո while ցիկլի մարմինը կատարվում է այնքան ժամանակ, քանի դեռ begin փոփոխվող հասցեն չի հասել end հասցեին։ Ցիլկի մարմնում մի ուրիշ for ցիկլ է, որը հիշող սարքի մոդելի վրա հերթականորեն աշխատեցնում է March գործողությունները և status բուլյան փոփոխականի մեջ է կուտակում հաջողությունների ու ձախողումների արդյունքը։
public boolean runOn(Memory mem)
{
// ամենամեծ հասցեն
final int maxaddr = mem.rows * mem.columns - 1;
// առաջին հասցեն
int begin = direction == INC ? 0 : maxaddr;
// վերջին հասցեն
int end = direction == INC ? maxaddr : 0;
boolean status = true;
while( begin != end + direction ) {
for( Operation op : operations ) {
boolean opok = op.runOn(mem, begin);
status = status && opok;
}
begin += direction;
}
return status;
}
Էլեմենտի տեքստային ներկայացումը ստանալու համար գրել եմ toString մեթոդը։
@Override
public String toString()
{
String ops = operations.stream()
.map(Operation::toString)
.collect(Collectors.joining(","));
String dr = (direction == INC ? "=>" : "<=");
return String.format("%s(%s)", dr, ops);
}
}
March ալգորիթմ
March ալգորիթմը March էլեմենտների հավաքածու է՝ խմբավորված ինչ-որ անունի տակ։Algorithm դասով մոդելավորել եմ March ալգորիթմը։
package march;
import memory.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* March ալգորիթմի մոդելը
*/
public class Algorithm {
// ալգորիթմի անունը
private String name = "";
// էլեմենտների ցուցակը
private List<Element< elements = null;
/**/
public Algorithm(String nm, List<Element> elems)
{
name = nm;
elements = new ArrayList<Element>();
elements.addAll(elems);
}
Ալգորիթմը հիշող սարքի մոդելի վրա գործարկվում է նորից runOn անունն ունեցող մեթոդի միջոցով։ for ցիկլը անցնում է բոլոր էլեմենտներով և դրանք գործարկում է՝ արգումենտում տալով հիշող սարքի հղումը։ Կատարման կումուլյատիվ արդյունքը ձևավորվում է status փոփոխականում։
public boolean runOn(Memory mem)
{
boolean status = true;
for( Element el : elements ) {
boolean elok = el.runOn(mem);
status = status && elok;
}
if( !status )
System.out.printf("'%s' ալգորիթմի կատարումը ձախողվեց։\n",
toString());
return status;
}
Վերջապես toString մեթոդը վերադարձնում է ալգորիթմի տեքստային ներկայացումը։
@Override
public String toString()
{
String estr = elements.stream()
.map(Element::toString)
.collect(Collectors.joining(";"));
return String.format("%s { %s }", name, estr);
}
}
Ալգորիթմների կառուցումը
Ես արդեն պատմեցիOperation, Element և Algorithm դասերի մասին։ Դրանցով կարելի է կազմել թեսթավորման ալգորիթմներ և դրանք գործարկել անսարքությունների մոդելներ պարունակող հիշող սարքի մոդելի վրա։ Բայց միայն նշված դասերի կոնստրուկտորներն օգտագործելով անհարմար է կառուցել մեծ ալգորիթմներ։ Օրինակ, միայն A0 { =>(W0); =>(W1,R1) } ալգորիթմը կառուցելու համար պետք է գրել հետևյալ Java կոդը։
List<Operation> ops0 = new ArrayList<>();
ops0.add( new Operation('W', '0') );
List ops1 = new ArrayList<>();
ops1.add( new Operation('W', '1') );
ops1.add( new Operation('R', '1') );
Element el0 = new Element(Element.INC, ops0);
Element el1 = new Element(Element.INC, ops1);
List els0 = new ArrayList<>();
els0.add(el0);
els0.add(el1);
Algorijthm al0 = new Algorithm("A0", els0);
Շատ ավելի հարմար կլիներ, որ ալգորիթմը կառուցվեր մարդուն հարմար տեքստային ներկայացումից։ Դրա համար march փաթեթում ավելացրել եմ Parser դասը, որի parse մեթոդը ստանում է March ալգորիթմի տեքստային ներկայացումը և վերադարձնում է կառուցված Algorithm օբյեկտի հղումը։
package march;
import java.util.ArrayList;
import java.util.List;
public class Parser {
private char[] source = null;
private int position = 0;
public Algorithm parse(String src) throws Exception
{
source = src.replaceAll("\\s+", "").toCharArray();
position = 0;
return parseAlgorithm();
}
Parser դասը մի պարզագույն քերականական վերլուծիչ է, որը «ճանաչում» է March ալգորիթմի վերը բերված քերականությունը․
Algorithm = IDENT '{' Element { ';' Element } '}'.
Element = ('=>' | '<=') '(' Operation { ',' Operation } ')'.
Operation = ('W' | 'R') ('0' | '1').
Քերականական երեք կանոններից յուրաքանչյուրի համար ստեղծված են համապատասխան մեթոդները՝ parseAlgorithm, parseElement և parseOperation։ Սրանցից ամեն մեկը վերլուծում է քերականությամբ իր համար որոշված հատվածը և վերադարձնում է Algorithm, Element կամ Operation օբյկտի հղում։
private Algorithm parseAlgorithm() throws Exception
{
char[] name = {0, 0};
if( !Character.isUpperCase(source[position]) )
throw new Exception("Սպասվում է լատիներեն մեծատառ։");
name[0] = source[position++];
if( !Character.isDigit(source[position]) )
throw new Exception("Սպասվում է թվանշան։");
name[1] = source[position++];
if( source[position++] != '{' )
throw new Exception("Սպասվում է '{'։");
List<Element> elements = new ArrayList<>();
elements.add(parseElement());
while( source[position] == ';' ) {
++position;
elements.add(parseElement());
}
if( source[position++] != '}' )
throw new Exception("Սպասվում է '}'։");
return new Algorithm(new String(name), elements);
}
private Element parseElement() throws Exception
{
int direction = 0;
if( source[position] == '=' && source[position+1] == '>' )
direction = 1;
else if( source[position] == '<' && source[position+1] == '=' )
direction = -1;
else
throw new Exception("Սպասվում է '=>' կամ '<=։");
position += 2;
if( source[position++] != '(' )
throw new Exception("Սպասվում է '('։");
List<Operation> operations = new ArrayList<>();
operations.add(parseOperation());
while( source[position] == ',' ) {
++position;
operations.add(parseOperation());
}
if( source[position++] != ')' )
throw new Exception("Սպասվում է ')'։");
return new Element(direction, operations);
}
private Operation parseOperation() throws Exception
{
char code = source[position++];
if( code != 'W' && code != 'R' )
throw new Exception("Անծանոթ գործողություն");
char data = source[position++];
if( data != '0' && data != '1' )
throw new Exception("Գործողության սխալ արժեք");
return new Operation(code, data);
}
Իհարկե, հարմար կլիներ Parser դասին ավելացնել ևս մի parse մեթոդ, որը ստանա ալգորիթմների ցուցակ և վերադարձնի Algorithm օբյեկտների հղումների ցուցակ։
Monday, March 31, 2014
Java և Kawa: Տեքստի տրոհումը Scanner դասի օգնությամբ
Scvanner դասը, որը կարելի է կանոնավոր արտահայտությունների միջոցով կարգավորել տեքստից բառեր կարդալու համար։ Բայց այս գրառման մեջ ուզում եմ նաև Java-ի HashMap բառարանի օգտագործումը համեմատ ել JVM վիրտուալ մեքենայով աշխատող GNU Kawa լեզվի (Scheme լեզվի իրականացում) hashtable բառարանի օգտագործման հետ։Նախ ներկայացնեմ Java տարբերակը (օգտագործված է JDK 8-ը)։
package wordcount;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Scanner;
/**/
public class WordCount {
/**/
private static void readFile( String name, HashMap<String,Integer> words )
{
// տրված անունով ֆայլի համար ստեղծել Scanner օբյեկտ
try( Scanner scan = new Scanner(new File(name)) ) {
// բացի մեծատառերից ու փոքրատառերից ամեն ինչ համարել բաժանիչ
scan.useDelimiter( "[^A-Za-z]+" );
// քանի դեռ Scanner-ից կարելի է կարդալ
while( scan.hasNext() ) {
// կարդալ հերթական բառը
String wo = scan.next().toLowerCase();
// գտնել կարդացած բառի արտապատկերումը բառարանում
Integer co = words.get(wo);
// եթե այդ բառը բառարանում չկա, ավելացնել այն՝ 1 քանակով,
// իսկ եթե կա՝ քանակն ավելացնել 1-ով
words.put(wo, co == null ? 1 : co + 1);
}
}
catch( FileNotFoundException ex ) {
System.err.println(ex.getMessage());
}
}
/**/
public static void main(String[] args)
{
// ստեղծել String->Integer արտապատկերում
HashMap<String,Integer> words = new HashMap<>();
// կարդալ ֆայլի պարունակությունը words բառարանի մեջ
readFile("~/Projects/martineden.txt", words);
// բառ-քանակ զույգերն արտածել ստանդարտ արտածման հոսքին
words.forEach( (k,v) -> System.out.printf("%s\t%s\n", k, v) );
}
}
Հիմա ցույց տամ, թե ինչպես եմ Kawa լեզվի միջոցներով կարդում ֆայլի բառերի հաջորդականությունը և կազմում դրանց հաճախությունների բառարանը։ Նախ սահմանեմ
read-all-words ֆունկցիան, որն արգումենտում ստանում է Java լեզվի Scanner օբյեկտը և վերագարձնում է տեքստի բառերի հաճախությունների բառարանը՝ որպես Scheme լեզվի hashtable օբյեկտ։
(define (read-all-words sca :: Scanner)
(define (read-all-words-rec ht)
(when (invoke sca 'hasNext)
(add-to-table (string-downcase (invoke sca 'next)) ht)
(read-all-words-rec ht)))
(let ((words (make-hashtable string-hash string=?)))
(read-all-words-rec words)
words))
read-all-words ֆունկցիայի համար սահմանված է read-all-words-rec լոկալ ռեկուրսիվ ֆունկցիան, որը Scanner օբյեկտից կարդում է մեկ բառ, այդ բառի բոլոր մեծատառերը դարձնում է փոքրատառ՝ string-downcase ֆունկցիայով, ապա ավելացնում է արգումենտում տրված բառարանում։ Բառարանը ստեղծվում է որպես read-all-words ֆունկցիայի լոկալ օբյեկտ՝ make-hashtable ֆունկցիայով։ Բառը բառարանում ավելացնող add-to-table ֆունկցիան սահմանված է հետևյալ կերպ․
(define (add-to-table w table)
(let ((co (hashtable-ref table w 0)))
(hashtable-set! table w (+ co 1))))
read-file ֆունկցիան, որ կսահմանեմ ստորև, տրված ֆայլի անունի համար ստեղծում է մի Scanner օբյեկտ և "[^A-Za-z]+" արտահայտությունը սահմանում է որպես դրա բաժանիչ։ Հետո read-all-words ֆունկցիայով ստանում է բառերի հաճախությունների բառարանը, այդ բառարանից կազմում է կետով զույգերի (dotted pair) ցուցակ, և վերադարձնում է այդ վերջին ցուցակն՝ ըստ բառերի այբբենական կարգի կարգավորած։
(define (read-file name ::) (let ((sca (Scanner:new (File:new name)))) (invoke sca 'useDelimiter "[^A-Za-z]+") (let-values (((ks vs) (hashtable-entries (read-all-words sca)))) (invoke sca 'close) (list-sort (lambda (a b) (stringlist ks) (vector->list vs))))))
Friday, March 21, 2014
Java: Անանուն ֆունկցիաները Java 8 լեզվում
Իմ բլոգի «Բարձր կարգի ֆունկցիաներ և անանուն ֆունկցիաներ» գրառման մեջ ես ֆունկցիայի ինտեգրալի թվային հաշվման օրինակով համեմատել էի Common Lisp և C++11 լեզվի անանուն ֆունկցիաները։ Այս գրառման մեջ ուզում եմ նույն այդ օրինակը ցույց տալ Java 8 լեզվի անանուն ֆունկցիաների օգտագործմամբ։
Մաթեմատիկական ֆունկցիան, որ կարելի է ինտեգրել, իրականացնում է
MathFunc ինտերֆեյսը։ Այն սահմանված է MathFunc.java ֆայլում։
package integral;
public interface MathFunc {
public double apply( double x );
}
Թվային ինտեգրման մեթոդն իրականացնում է Method ինտերֆեյսը, որը սահմանված է Method.java ֆայլում՝ հետևյալ կերպ։
package integral;
public interface Method {
public double apply( MathFunc f, double a, double b );
}
Սահմանեմ Integral դասը և սահմանեմ այդ դասի evaluate մեթոդը, որն արգումենտում սպասում է ինտեգրման մեթոդը, ինտեգրվող ֆունկցիան, ինտեգրման միջակայքը և միջակայքի՝ հատվածների տրոհման գործակիցը։ Հաշվելու եղանակը պարզագույնն է. meth օբյեկտի apply մեթոդին են փոխանցվում ինտեգրվող ֆունկցիան և այն փոքր հատվածը, որի վրա նշված թվային մեթոդով հաշվվում է ինտեգրալը։
package integral;
public class Integral {
public static double evaluate(Method meth, MathFunc func,
double left, double right, double epsilon)
{
double result = 0.0;
for( double x = left; x < right; x += epsilon )
result += meth.apply(func, x, x + epsilon);
return result;
}
}
Որպեսզի, օրինակ, Integral դասի evaluate ստատիկ մեթոդի օգնությամբ հաշվեմ \(f(x)=3x^2-2x+1\) ֆունկցիայի ինտեգրալը, պետք է նախ՝ \(f(x)\) ֆունկցիան սահմանեմ որպես MathFunc ինտերֆեյս իրականացնող օբյեկտ, իսկ ինտեգրման մեթոդը, օրինակ սեղանների կանոնը, սահմանեմ որպես Method ինտերֆեյսն իրականացնող օբյեկտ։Սահմանեմ
Calc դասը՝ main մուտքի կետով։
package integral;
public class Calc {
/**/
public static void main( String[] args )
{
// ինտեգրման մեթոդ
Method Simple = new Method() {
@Override
public double apply( MathFunc f, double a, double b )
{ return (b - a) * f.apply((b + a) / 2); }
};
// ինտեգրվող ֆունկցիա
MathFunc Sin = new MathFunc() {
@Override
public double apply( double x )
{ return 3*x*x - 2*x + 1; }
};
// ինտեգրալի հաշվումն ու արտածումը
double r0 = Integral.evaluate(Simple, Sin, 0, 1, .0001);
System.out.println( r0 );
}
}
Բայց սա հնացած եղանակն է։ Անանուն ֆունկցիաների օգտագործմամբ գրվում է շատ ավելի համառոտ, գեղեցիկ ու հասկանալի կոդ։ Ստորև բերված main ֆունկցիայում ինտեգրման մեթոդն ու ինտեգրվող ֆունկցիան evaluate ֆունկցիային փոխանցված են որպես անանուն ֆունկցիաներ։
package integral;
public class Calc {
/**/
public static void main( String[] args )
{
double r1 = Integral.evaluate( (f,a,b)->(b-a)*f.apply((b+a)/2),
(x)-> 3*x*x - 2*x + 1, 0, 1, .0001 );
System.out.println( r1 );
}
}
(արգումենտների ցուցակ) -> ֆունկցիայի մարմին
Օրինակ, թվային մոտավոր ինտեգրման սեղանների կանոնի բանաձևը վերը բերված օրինակում սահմանված է ահա այսպես․
(f, a, b) -> (b - a) * f.apply((b + a) / 2)Կարելի է անանուն ֆունկցիան կապել որևէ փոփոխականի հետ, օրինակ, թվային մոտավոր ինտեգրման Սիմպսոնի կանոնի բանաձևը սահմանված է որպես անանուն ֆունկցիա և վերագրված է
simpson փոփոխականին․
public static Method simpson = (f, a, b) -> ((b - a) / 6) * (f.apply(a) + f.apply(b) + 4*f.apply((a+b)/2));Ինտեգրալը հաշվելիս կարելի է
evaluate ֆունկցիային փոխանցել simpson փոփոխականը․
double r2 = Integral.evaluate( simpson, (x)-> 3*x*x - 2*x + 1, 0, 1, .0001 );
Առայժմ այսքանը։ Ես դեռ նոր եմ ուսումնասիրում Java 8-ի անանուն ֆունկցիաները։ Հետքգայում միգուցե նորից անդրադառնամ այս թեմային ու ներկայացնեմ ավելի հետաքրքիր օրինակներ։
Friday, September 20, 2013
GNU/bytecode գրադարանի օգտագործման օրինակ
Ծրագրավորման լեզուների կոմպիլյացիայի հերցերն ուսումնասիրելիս ես տևական ժամանակ փնտրում էի մի գրադարան, որը հարավորություն կտար առանց մանրամասնությունների մեջ մտնելու գեներացնել Java վիրտուալ մեքենայի class ֆայլեր։ Առաջին որոնումները բերեցին ASM և BCEL գրադարաններին և Jasmin «Java ասեմբլերին»։ Վերջինս հնարավորություն է տալիս մնեմենիկ հրամաններով հրագրավորել ալգորիթմը, ապա այն «ասեմբլացնել» ու ստանալ վիրտուալ մեքենայի բայթ-կոդ՝ class ֆայլ։
Բայց, տարբեր պատճառներով, այդ գրադարաններից ոչ մեկը ես հարմար չգտա սովորելու համար։ Հետագա որոնումների ընթացքում հանդիպեցի GNU/bytecode գրադարանին, որ Scheme ծրագրավորման լեզվի Kawa իրականացման մաս է կազմում։
Այս գրառման մեջ ես ուզում եմ ֆակտորիալի և ամենամեծ ընդհանուր բաժանարարի հաշվման պարզ օրինակներով ցույց տալ, թե ինչպես կարելի է գեներացնել այդ երկու ալգորիթմները պարունակող class ֆայլը։
Նախ սկսեմ Java տարբերակից.
public class Algorithms {
public static int factorial( int n )
{
if( n == 1 ) return 1;
return n * factorial( n - 1 );
}
public static int gcd( int n, int m )
{
while( n != m )
if( n > m )
n -= m;
else
m -= n;
return n;
}
}
Այս դասի նկարագրությունը Algorithms.java ֆայլի մեջ գրառելուց և javac կոմպիլյատորով թարգմանելուց հետո ստացվում է Algorithms.class ֆայլը։ class ֆայլը կարելի է javap դիզասեմբլերով հետ թարգմանել ու տեսնել թե Java լեզվի կոմպիլյատորն ինչ հրամաններ է գեներացրել։ Այդ բոլոր հրամանների նկարագրությունը կարելի է գտնել Java վիրտուալ մեքենայի նկարագրության մեջ։
Compiled from "Algorithms.java"
public class Algorithms {
public Algorithms();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static int factorial(int);
Code:
0: iload_0
1: iconst_1
2: if_icmpne 7
5: iconst_1
6: ireturn
7: iload_0
8: iload_0
9: iconst_1
10: isub
11: invokestatic #2 // Method factorial:(I)I
14: imul
15: ireturn
public static int gcd(int, int);
Code:
0: iload_0
1: iload_1
2: if_icmpeq 24
5: iload_0
6: iload_1
7: if_icmple 17
10: iload_0
11: iload_1
12: isub
13: istore_0
14: goto 0
17: iload_1
18: iload_0
19: isub
20: istore_1
21: goto 0
24: iload_0
25: ireturn
}
Այս արտածումը տրված է JVM-ի հրամաններ մնեմոնիկ ներկայացմամբ, որոնց բացատրությունները բերված են JVM Specification էջում։
Հիմա ցույց տամ, թե ինչպես եմ gnu.bytecode գրադարանի օգտագործմամբ ստեղծում factorial և gcd ալգորիթմները պարունակող class ֆայլը։ Արդեն նշեցի, որ gnu.bytecode գրադարանը Kawa լեզվի իրականացման մաս է։ Չնայած որ կարելի է կոդից առանձնացնել միայն gnu.bytecode-ն և կառուցել առանձին *.jar ֆայլ, ես կօգտագործեմ հենց kawa-1.13.jar ֆայլը։
ՈՒրեմն, նախ ներմուծում եմ gnu.bytecode գրադարանը․ import gnu.bytecode.*;
Հետո սահմանում եմ AlgoEx դասն իր main մեթոդով․
public class AlgoEx {
public static void main(String[] args) throws Exception
{
Հետո ստեղծում եմ ClassType դասի օբյեկտ, որը ներկայացնում է Java վիրտուալ մեքենայի դասը։ Այնուհետև դասի համար որպես ծնող նշում եմ java.lang.Object դասը, իսկ որպես տեսանելիության մոդիֆիկատոր նշում եմ public - Access.PUBLIC հոստատունի օգնությամբ։
// class `Algorithms'
ClassType clo = new ClassType("Algorithms");
clo.setSuper("java.lang.Object");
clo.setModifiers(Access.PUBLIC);
Քանի որ և՛ factroial, և՛ gcd մեդոդները սահմանելու եմ public ու static մոդիֆիկատորներով, այստեղ սահմանել եմ pubstat հաստատունը․
final int pubstat = Access.PUBLIC | Access.STATIC;
Հետո clo դասում ավելացնում եմ նոր մեթոդ՝ "factorial" անունով և "(I)I" սիգնատուրայով։ addMethod մեթոդի վերադարձրած հղումև վերագրում եմ Method տիպի mfac փոփոխականին։
// method `factorial'
Method mfac = clo.addMethod("factorial", "(I)I", pubstat);
CodeAttr դասը նախատեսված է բուն կոդի գեներացիայի համար։ Ստորև բերված բլոկում կառուցված է ամբողջ թվի ֆակտորիալը հաշվող ֆունկցիայի կոդի գեներացիան․
CodeAttr code = mfac.startCode();
code.pushScope();
code.emitLoad(code.getArg(0));
code.emitPushInt(1);
code.emitIfEq();
code.emitPushInt(1);
code.emitReturn();
code.emitElse();
code.emitLoad(code.getArg(0));
code.emitDup();
code.emitPushInt(1);
code.emitSub(Type.intType);
code.emitInvoke(mfac);
code.emitMul();
code.emitReturn();
code.emitFi();
code.popScope();
Նույն կերպ կառուցված է gcd մեթոդի կոդի գեներացիան։
// method `gcd'
Method mgcd = clo.addMethod("gcd", "(II)I", pubstat);
Label bw = new Label(code);
Label ew = new Label(code);
code = mgcd.startCode();
code.pushScope();
bw.define(code);
Variable n = code.getArg(0);
Variable m = code.getArg(1);
code.emitLoad(n);
code.emitLoad(m);
code.emitGotoIfEq(ew);
code.emitLoad(n);
code.emitLoad(m);
code.emitIfGt();
code.emitLoad(n);
code.emitLoad(m);
code.emitSub(Type.intType);
code.emitStore(n);
code.emitElse();
code.emitLoad(m);
code.emitLoad(n);
code.emitSub(Type.intType);
code.emitStore(m);
code.emitFi();
code.emitGoto(bw);
ew.define(code);
code.emitLoad(n);
code.emitReturn();
code.popScope();
Եվ վերջում writeToFile մեթոդով կառուցված դասի պարունակությունը գրվում է class ֆայլի մեջ։
// write class file
clo.writeToFile();
}
}
Հիմա թարգմանեմ այս ֆայլը Java կոմպիլյատորով և գործարկեմ այն, որպեսզի ստանամ Algorithms.class ֆայլը։
$ javac -classpath .:kawa-1.13.jar AlgoEx.java $ java -classpath .:kawa-1.13.jar AlgoEx
Այս երկու հրամանների կատարումից հետո գեներացվում է Algorithms.class ֆայլը։ Կարելի է javap դիզասեմբլերով տեսնել այս ֆայլի պարունակությունը։ Բայց ես կգրեմ մի տեստային ֆայլ, որը կօգտագործի Algwrithms դասի factorial և gcd ֆունկցիաները։
public class Test {
public static void main(String[] args)
{
System.out.println(Algorithms.factorial(10));
System.out.println(Algorithms.gcd(123,31));
}
}
Այս դասի թարգմանությունն ու կատարումը ցույց է տալիս, որ gnu.bytecode գրադարանի օգնությամբ կառուցված class ֆայլը ճիշտ է․
$ javac Test.java $ java Test 3628800 1
Friday, March 29, 2013
Այլ ծրագրի աշխատեցնելը Java ծրագրից
1. NetBeans աշխատանքային միջավայրում ստեղծենք
ScriptRunner անունով պրոյեկտը և նրա լռելությամբ ստեղծված scriptrunner փաթեթում ավելացնենք JFrame դասից ժառանգված MainWindow անունով պատուհանը։ 2. Պատուհանի վրա ավելացնենք արտաքին սկրիպտի ճանապարհը նշելու, այն աշխատեցնելու և արդյունքներն արտածելու հետևյալ երեք օբյեկտները։
txtScript : JTextField- սկրիպտի կամ հրամանի ճանապարհը նշելու դաշտ,btnRun : JButton- աշխատանքը սկսելու կոճակ,areaResults : JTextArea- արդյունքներն արտածելու տիրույթ:
3. "Run" կոճակի կոնտեքստային մենյուից ընտրենք Events->Action->actionPerformed գործողությունը, որի հետևանքով պատուհանի կոդի հատվածում կստեղծվի
btnRunActionPerformed անունով մեթոդի դատարկ մարմինը։ Այս մեթոդը կանչվելու է, երբ սեղմենք "Run" կոճակը։
private void btnRunActionPerformed(java.awt.event.ActionEvent evt) {
//
}
Արտաքին ծրագիրն աշխատեցնում եմ
Process դասի օգնությամբ, որը հնարավորություն է տալիս իր getInputStream մեթոդից ստեղծել կարդալու հոսք և կարդալ արտաքին ծրագրի արտածման ու սխալների ստանդարտ հոսքերի արտածումները։ Օրինա, եթե պետք է Java ծրագրից աշխատեցնել ls ծրագիրը, ապա պետք է նախ ProcessBuilder դասի կոնստրուկտորին տալ աշխատեցվելիք ծրագրի անունը, ապա կանչելով նրա start մեթոդը, ստեղծել նոր պրոցես։
Process ps = (new ProcessBuilder("ls")).start();
InputStream is = ps.getInputStream();
Պրոցեսի տված հաղորդագրությունները սինխրոն կարդալու համար ստեղծել եմ մի thread, որը կարդում է պրոցեսի արտածման հոսքի հետ կապված ընթերցման հոսքի պարունակությունը և արտածում է areaResults դաշտում։Ահա
btnRunActionPerformed մեթոդի իրականացումը։
private void btnRunActionPerformed(java.awt.event.ActionEvent evt) {
try {
// պրոցեսի ստեղծում
ProcessBuilder pb = new ProcessBuilder(txtScript.getText());
// սխալների հոսքն ուղղել stdout-ի վրա
pb.redirectErrorStream(true);
// պրոցեսի գործարկում
Process procFlow = pb.start();
// վերցնել պրոցեսի արտածման հոսքը
InputStream inp = procFlow.getInputStream();
final BufferedReader reader =
new BufferedReader(new InputStreamReader(inp));
// thread-ի ստեղծում
Thread outputThread = new Thread() {
@Override
public void run()
{
try {
// ապահովել reader-ը,
synchronized(reader) {
String text = "";
// քանի դեռ կարդալու բան կա, կարդալ
while(null != (text = reader.readLine())) {
// տեքստը կցել areaResults-ի պարունակության պոչից
areaResults.append(text + "\n");
// կուրսորը տեղափոխել տեքստի վերջը
areaResults.setCaretPosition(areaResults.getText().length());
}
}
}
catch (Exception e) {}
}
};
// կարդացող thread-ի գործարկում
outputThread.start();
}
catch(IOException ex) {
JOptionPane.showMessageDialog(this, ex.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
}
}
#!/bin/bash for m in 1 2 3 4 5 6 7 8 9 10 11 12 do echo Message number: $m sleep 2 doneԾրագիրն աշխատեցնելուց հետո
txtScript դաշտում ներմուում եմ սկրիպտի ճանապարհը ու սեղմում եմ Run կոճակը։ Արդյունքն այն է, ինչ ես սպասում էի։
Wednesday, December 12, 2012
Java: Գործիքների տեղադրում և գործարկում
Այս գրառման մեջ ես ցույց կտամ, թե ինչպես Ubuntu GNU/Linux համակարգում տեղադրել Java ծրագրավորման լեզվով ծրագրավորելու համար անհրաժեշտ գործիքները՝ JRE, JDK և ծրագրավորման ինտեգրացված միջավայր NetBeans։
Տեղադրելու համար ես ընտրել եմ Java ծրագրավորման լեզվի OpenJDK իրականացումը։ Այն Ubuntu օպերացիոն համակարգում տեղադրելու համար պետք է տերմինալից ներածել հետևյալ հրամանները
$ sudo apt-get install openjdk-6-jre $ sudo apt-get install openjdk-6-jdkՏեղադրելուց հետո կարող ենք ստուգել կոմպիլյատորի ու վիրտուալ մեքենայի առկայությունը՝ պահանջելով արտածել տեղադրված տարբերակի մասին տեղեկությունները։
$ javac -version javac 1.6.0_24 $ java -version java version "1.6.0_24" OpenJDK Runtime Environment (IcedTea6 1.11.5) (6b24-1.11.5-0ubuntu1~12.04.1) OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)Հիմա կարող ենք թարգմանել ու կատարել թվի ֆակտորիալը հաշվող ծրագիրը։
NetBeans ծրագրավորման ինտեգրացված միջավայրը տեղադրելու համար այստեղից պետք է ներբեռնել համապատասխան փաթեթը։ (Ես ընտրել եմ NetBeans 7.2.1 Java SE (77 MB) տարբերակը, որի ֆայլը կոչվում է
netbeans-7.2.1-ml-javase-linux.sh։) Այնուհետև, եթե բեռնված ֆայլը կատարվող չէ, ապա chmod հրամանով պետք է այն դարձնել կատարվող և գոծարկել հրամանային տողից։ Տեղադրման պրոցեսը շատ պարզ է (տեղադրող ծրագիրը ճանաչում է համակարգում տեղադրած Java լեզվի իրականացումը):Տեղադրելուց հետո, երբ գործարկենք NetBeans միջավայրը, կտեսնենք ահա այսպիսի պատկեր․
Windwows օպերացիոն համակարգի համար կարելի է Oracle կազմակերպության սայթից մեկ փաթեթով ներբեռնել Java լեզվի իրականացումն ու NetBeans միջավայրը։ Այդ փաթեթի տեղադրումը նույնպես շատ պարզ է։
1. NetBeans միջավայրի «File» մենյուից ընտրում ենք «New Project...» կետը, որից հետո բացված պատուհանում «Categories» ցուցակից ընտրում ենք «Java» կետը, իսկ «Projects» ցուցակից՝ «Java Application» կետը։ Ապա սեղմում ենք «Next» կոճակը։
2. Հաջորդ քայլում փոխարինում ենք «Project Name» դաշտի արժեքը «Factorial» բառով և սեղմում ենք «Finish» կոճակը։
NetBeans միջավայրը ստեղծում է
factorial անունով փաթեթ և գեներացնում է Factorial.java ֆայլի մակետը, որը պարունակում է Factorial դասի նախնական սահմանումը՝ main մեթոդով։
Հիմա տեսնենք, թե ի՞նչ է գեներացրել NetBeans-ը
Factorial դասի համար։ Եթե դեն նետենք մեկնաբանությունները, որոնք, ի դեպ, ժառանգվել են ծրագրավորման C լեզվից, ապա կտեսնենք հետևյալը․
package factorial;
public class Factorial {
public static void main(String[] args)
{
}
}
Այստեղ նորություն է միայն package factorial; տողը։ Այն տեղեկացնում է, որ Factorial դասը պատկանում է factorial փաթեթին։ Փաթեթը Java լեզվում ծրագրային բաղադրիչների խմբավորման միջոց է։ Այս պարզագույն խնդրում ունենք միայն մեկ փաթեթ՝ factorial, որը պարունակում է միակ Factorial դասը։ Հետագա օրինակներում կտեսնենք, թե ինչպես մեծ նախագիծը փաթեթների օգնությամբ տրոհել տրամաբանական միավորների։Այս գեներացված
main ֆունկցիայի մարմինը լրացնենք ֆակտորիալի հաշվման ալգորիթմով և սեղմենք NetBeans միջավայրի գործիքների տողի Ծրագրի կատարվելուց հետո NetBeans միջավայրի ներքևի մասում բացվում է արտածումների պատուհանը՝ Output, որում էլ արտածվում են ծրագրի աշխատանքի արդյունքները։
Այսքանով ես կսահմանափախեմ գործիքներին վերաբերող ամենաանհրաժեշտ տեղեկությունները։ Հետագա խնիրները ներկայացնելիս արդեն գործիքների այս կամ այն հնարավորություններին հղումներ կկատարեմ միայն անհրաժեշտության դեպքում։
Tuesday, December 11, 2012
Java: Առաջին ծրագիրը
Hello, World!" տեքստն արտծող ծրագիրը։ Մի կողմ թողնենք այն և որպես առաջին ծրագիր դիտարկենք տրված դրական ամբողջ թվի ֆակտորիալը հաշվող և արտածող ծրագիրը։ Այն, կարծում եմ, և՛ ավելի հետաքրքիր է, և՛ ավելի խոսուն։Եվ այսպես. տրված n դրական ամբողջ թվի ֆակտորիալը դա 1-ից
n ամբողջ թվերի արտադրյալն է։ Այն հաշվելու համար պարզապես պետք է կազմակերպել մի ցիկլ՝ կրկնություն, որն անցնում է 1..n թվերով և կուտակում է դրանց արտադրյալը։ Փսևդոկոդով գրելու դեպքում, օրինակ 12 թվի ֆակտորիալը հաշվելու համար, կունենանք ահա այսպիսի ծրագիր.n = 12 prod = 1 WHILE n > 0 DO prod = prod * n n = n - 1 END PRINT prodJava ծրագրի կատարումը սկսվում է գլխավոր դասի
main անունով ստատիկ մեթոդից։ Դասը սահմանվում է class ծառայողական բառով, որին հետևում է դասի անունը, ապա մեթոդների ու դաշտերի սահմանումները։ Օրինակ, Factorial անունով դասը կարող ենք սահմանել հետևյալ կերպ.
public class Factorial {
...
}
Որտեղ public բառն ասում է, որ տվյալ դասը կարող են օգտագորվծել այլ փաթեթներում (փաթեթների մասին քիչ ավելի ուշ)։Factorial դասի համար սահմանենք
main ստատիկ մեթոդը.public class Factorial {
public static void main(String[] args)
{
...
}
}
Նորից public ծառայողական բառն ասում է, որ main մեթոդը տեսանելի է Factorial դասից դուրս։ static բառն ասում է, որ այս մեթոդն ընդհանուր է Factorial դասի մոլոր նմուշների համար (սրանք էլ մանրամասնորեն կքննարկենք ավելոի ուշ)։ void բառն ասում է, որ main մեթոդը որևէ արժեք չի վերադարձում։ main մեթոդի արգումենտների ցուցակում գրված "String[] args" արտահայտությունը նշում է, որ այս մեթոդը սպասում է (ընդունում է, ակնկալում է) մեկ արգումենտ՝ տողերի միաչափ զանգված (վեկտոր)։ main մեթոդի կատարման ժամանակ նրա արգումենտն արժեքավորվում է հրամանային տողի պարունակությամբ (այս մասին էլ ավելի ուշ)։
Հիմա սկսենք ֆակտորիալի հաշվարկը։ Ասացինք, որ աշխատելու ենք ամբողջ թվերի հետ։ Հայտարարենք
n և prod ամբողջ թվերը՝ առաջինն արժեքավորելով 12 արժեքով, իսկ երկրորդը՝ 1 արժեքով։
int n = 12, prod = 1;Կազմակերպենք ցիկլ, որը կատարվում է քանի դեռ ճշմարիտ է
n > 0 պայմանը։ Իսկ ցիկլի մարմնում հաշվարկվում է prod = prod * n արտադրյալը, և մեկով նվազեցվում է n փոփոխականի արձեքը։
Պայմանով ցիկլերը կազմակերպվում են
while կառուցվածքով։ Այն կատարում է իր մարմնում գրված հրամաններն այնքան ժամանակ, քանի դեռ ճշմարիտ է կրկնման պայմանը։
while( n > 0 ) {
prod = prod * n;
n = n - 1;
}
Տվյալ դեպքում ցիկլն անպայման կավարտվի, քանի որ կրկնությունների ընթացքում n դրական թվի արժեքը շարունակ նվազում է։ Եվ երբ ավարտվի ցիկլը, prod փոփոխականում կուտակված կլինի 1..n թվերի արտադրյալը։Եվ վերջապես, ինչպե՞ս արտածել հաշվարկման արդյուքները։ Java լեզվի ստանդարտ գրադարանի
System դասի out դաշտի println մեթոդը ստանդարտ արտածման հոսքի վրա դուրս է բերում իր արգումենտում տրված արժեքը։ prod փոփոխականի արժեքը արտածելու համար պետք է գրել.
System.out.println(prod);
Factorial.java անունով ֆայլ և նրա մեջ գրենք հետևյալը.
/*
First program in java
*/
public class Factorial {
public static void main(String[] args)
{
int n = 12, prod = 1;
while( n > 0 ) {
prod = prod * n;
n = n - 1;
}
System.out.println(prod);
}
}
Պահպանենք ֆայլը պրոյեկտների համար նախատեսված մի պանակում՝ նախապես այս օրինակի ֆայլերի համար ստեղծելով factorial ենթապանակը (այն ինձ մոտ home-ում ստեղծված Project/java-examples պանակում է)։
cd հրամանով փոխենք աշխատանքային պանակն այնտեղ, որտեղ պահպանված է Factorial.java ֆայլը, և, ենթադրելով, որ համակարգում արդեն տեղադրված է Java լեզվի կոմպիլյատորն (javac) ու վիրտուալ մեքենան (java), թարգմանենք մեր գրած ծրագիրը բայթ-կոդի։
$ javac Factorial.javaԵթե թարգմանության՝ կոմպիլյացիայի պրոցեսում սխալներ չեն հայտնաբերվել, ապա հենց նույն պանակում ստեղծվում է
Factorial.class անունով ֆայլ։ Սա մեր ծրագիրն է՝ Java ծրագրավորման լեզվից թարգմանած Java վիրտուալ մեքենայի բայթ-կոդերի։ Այն կատարելու համար պետք է կանչել Java վիրտուալ մեքենան՝ նրա արգումենտում տալով այն դասի անունը, որում սահմանված է main մեթոդը։ Մեր դեպքում դա միակ Factorial դասն է։
$ java FactorialՏերմինալին արտածվում է 479001600, որը, կարող ենք ստուգել և համոզվել, հենց 12 թվի ֆակտորիալն է։














