Kirjautuminen

Haku

Tehtävät

Keskustelu: Yleinen keskustelu: Ohjelmointi haaste

Sivun loppuun

jalski [31.01.2022 23:55:51]

#

Laske tekstitiedostossa olevat luvut siten, että erottelet luvun tyypin (int, float). Luvut voi olla eroteltu pilkulla, välilyönneillä, puolipisteeellä, tabulaattorilla tai rivinvaihdolla. Jos syötteessä on virhe niin ohjelman pitää osata tarkalleen kertoa rivi ja sarake missä virhe on tapahtunut. Luvuissa pitää olla E-notaatio tuettuna myös.

Alla oma toteutukseni 8th-ohjelmointikielellä:

\
\ scanner.8th
\
needs file/getc

ns?

ns: scanner

-1 constant EOF
10 constant LF
32 constant SPACE

0 constant ,SYM
1 constant ;SYM
2 constant INTSYM
3 constant FLOATSYM

[] constant special-chars

: scanner:__init__
  "file" rot m:!
  "column" 0 m:!
  "line" 1 m:!
  "char" SPACE m:!
  "number" 0 m:!
  "word" "" m:!
  drop ;

: scanner:__deinit__ ;

: new  \ file -- scanner
  ns:scanner G:new ;

: >char  \ n -- char
  "" swap s:+ ;

: line@  \ scanner -- scanner line
  "line" m:@ ;

: column@  \ scanner -- scanner column
  "column" m:@ ;

: char@  \ scanner -- scanner char
  "char" m:@ ;

: number@  \ scanner -- scanner string
  "number" m:@ ;

: word@  \ scanner -- scanner string
  "word" m:@ ;

: file@  \ scanner -- scanner file
  "file" m:@ ;

: line!  \ scanner n -- scanner
  "line" swap m:! ;

: column!  \ scanner n -- scanner
  "column" swap m:! ;

: char!  \ scanner char -- scanner
  "char" swap m:! ;

: number!  \ scanner n -- scanner
  "number" swap m:! ;

: word!  \ scanner s -- scanner
  "word" swap m:! ;

: accept  \ n a  -- n T
  ( over n:= ) a:map
  ' or false a:reduce ;

: digit?  \ n -- bool
  '0 '9 n:between ;

: alpha?  \ n -- bool
  dup >r 'a 'z n:between r@ 'A 'Z n:between or
  r> special-chars accept nip or ;

: read-char  \ scanner -- scanner
  char@
  LF n:= if
    line@ n:1+ line!
    0 column!
  then

  file@
  f:getc swap -rot char!
  swap f:eof? nip if
    EOF char!
  then

  char@ EOF n:= not if
    column@
    n:1+ column!
  then ;

\ Status
0 constant EMPTY
1 constant PARTIAL-FAIL
2 constant PARTIAL-OK
3 constant OK
4 constant ERROR

\ State
0 constant S0
1 constant IPART
2 constant FPART
3 constant ESIGN
4 constant EPART

: make-dword  \ lword hword -- dword
  0xffff n:band 16 n:shl swap
  0xffff n:band
  n:bor ;

: s2
  OK IPART 2 a:close extra! ;

: s3
  PARTIAL-FAIL FPART 2 a:close extra! ;

: s4
  PARTIAL-OK ESIGN 2 a:close extra! ;

: s5
  extra@ 0 OK a:! drop ;

: s6
  PARTIAL-FAIL FPART 2 a:close extra! ;

: s7
  PARTIAL-OK ESIGN 2 a:close extra! ;

: s8
  extra@ 0 OK a:! drop ;

: s9
  PARTIAL-OK ESIGN 2 a:close extra! ;

: s10
  PARTIAL-FAIL EPART 2 a:close extra! ;

: s11
  extra@ 1 EPART a:! drop ;

: s12
  extra@ 0 OK a:! drop ;

hex
\ hi-word valid character, low-word state
[ 20,9,30,31,32,33,34,35,36,37,38,39,2e,65,45,10030,10031,10032,10033,10034,
  10035,10036,10037,10038,10039,1002e,10065,10045,20030,20031,20032,20033,
  20034,20035,20036,20037,20038,20039,20065,20045,3002b,3002d,30030,30031,
  30032,30033,30034,30035,30036,30037,30038,30039,40030,40031,40032,40033,
  40034,40035,40036,40037,40038,40039] constant state-table
decimal

: actions:
  a:new '; parse
  /\s+/ s:/ ( s:len nip ) a:filter a:len if
    ( w:find a:push ) a:each! drop
  else
    drop
  then ;

actions: noop noop s2 s2 s2 s2 s2 s2 s2 s2 s2 s2
         s3 s4 s4 s5 s5 s5 s5 s5 s5 s5 s5 s5 s5
         s6 s7 s7 s8 s8 s8 s8 s8 s8 s8 s8 s8 s8
         s9 s9 s10 s10 s11 s11 s11 s11 s11 s11 s11
         s11 s11 s11 s12 s12 s12 s12 s12 s12 s12 s12
         s12 s12 ; constant action-table

: read-number  \ scanner -- scanner type
  "" word!

  repeat
    char@ swap word@ rot s:+ word!
    read-char
    char@ dup >r digit? r@ '. n:= or r@ [69, 101, 43, 45, 46] accept nip or not r> EOF n:= or if
      word@ >n number!
      break
    else
      char@ over extra@ nip 1 a:@ nip make-dword state-table swap ' n:= a:indexof nip null? if
        drop
        extra@ 0 ERROR a:! drop
      else
        action-table case
      then
      extra@ 0 a:@ nip ERROR n:= if
        column@ swap line@ nip "Line:%d Column:%d, Not a valid number" s:strfmt throw
      then
    then
  again

  extra@ a:open make-dword
  [0x10003, 0x20003, 0x20002, 0x30002, 0x40003, 0x40002]
  swap ' n:= a:indexof nip null? if
    drop column@ swap line@ nip "Line:%d Column:%d, Not a valid number" s:strfmt throw
  else
    [ @INTSYM, @FLOATSYM, @FLOATSYM, @FLOATSYM, @FLOATSYM, @FLOATSYM ] caseof
  then ;

: partial-fail  \ state --
  PARTIAL-FAIL swap 2 a:close extra! ;

[
  ( char@ digit?  ), ( OK IPART 2 a:close extra! read-number ),
  ( char@ '+ n:=  ), ( IPART partial-fail read-number ),
  ( char@ '- n:=  ), ( IPART partial-fail read-number ),
  ( char@ '. n:=  ), ( FPART partial-fail read-number ),
  ( char@ ', n:=  ), ( read-char ,SYM ),
  ( char@ '; n:=  ), ( read-char ;SYM ),
  ( char@ EOF n:= ), ( EOF ),
  ( char@ >char swap column@ swap line@ nip "Line:%d Column:%d, Illegal character: %s" s:strfmt throw )
] var, symbols

: get-token  \ scanner -- scanner token
  \ skip white space
  repeat
    char@ EOF n:= not swap char@ SPACE n:> not rot and if
      read-char
    else
      break
    then
  again

  symbols @ a:when ;

ns
\
\ nc.8th
\
"scanner.8th" f:include

var ints
var floats

[ ' noop ,           \ ,SYM
  ' noop ,           \ ;SYM
  ( 1 ints n:+! ) ,  \ INTSYM
  ( 1 floats n:+! )  \ FLOATSYM
] var, possible-tokens


: app:main
  0 args "numbers.txt" ?: f:open-ro scanner:new

  repeat
    scanner:get-token
    dup scanner:EOF n:= if
      2drop break
    else
      possible-tokens @ case
    then
  again

  floats @ ints @ "Number of ints: %d\nNumber of floats: %d\n" s:strfmt .
  bye ;

Jere Sumell [01.02.2022 06:41:37]

#

Ohjelmoin aluksi tuollaisen lukugeneraattorin Javalla, kun ajattelin Javalla ohjelmoida tämän jalskin esittämän haasteen, vaikka varmaan se kovinkaan tehokas ole välttämättä olioperusteisena suoritusajaltaan, mutta pitää demoaineisto olla ensin koottuna.

Tämä nyt tulostaa satunnaisen käyttäjän määrittelemän kokoisen tiedostoon tallennettavan demoaineiston niillä kriteereillä, joita jalski on antanut, luotavan demo-populaation koko on suhteutettu koko aineiston määrään, ja tiedoston "sample.txt" -alkioiden lukumäärän saa itse määritellä, olen kommentoinut koodin kohtiin, samaten mitä virhesyotteiden määrän voi määritellä, jos tykkää käyttää sitten siinä varsinaisessa ohjelmassa ohjelmoidessaan sen virheen sijainnin tallentamisen johonkin muuttujaan, ja jos niitä on useampia, jos asettaa 0 ERRORS -vakiomuuttujan arvoksi, niin sittenhän tiedostoon ei tulosteta, kuin läpimeneviä arvoja.

Koodini, olkaa hyvä, saa käyttää vapaasti oman demopopulaation generointiin ja helpottaa huomattavasti, jos ohjelmoitte tätä jalskin haastetta.

EDIT: Muokkasin koodiani modulaarisempaan suuntaan, niin nyt kokonaislukuja ja erikokoisia liukulukuja on satunnaisesti voi olla sekaisin samaten, kuten erotinmerkkejä, mitä jalski antoi ehdoiksi.

Lopullinen koodini, käyttäkää tätä, kun ohjelmoitte itse haasteen tiedostonlukuohjelmaa, demopopulaation voi kätevästi "sample.txt" -tiedostoon luoda ajamalla tämä:

import java.util.Random;
import java.util.ArrayList;
import java.io.*;


public class Lukugeneraattori {

	private ArrayList<String> lista;
	private Random r;
	private File tiedosto;
	private FileWriter fw;
	private BufferedWriter bwriter;
	private final String[] DE = {",",";","\t","\n"};


	//Muokkaa näitä tietoja vaikuttaaksesi demoaineiston kokoon, joka tallennetaan tiedostoon.
	private final int SIZE = 100;
	private final int ERRORS = 0;
	/////////////////////////////////////////////////////////////////////////////////////////

	public Lukugeneraattori() {
		r = new Random();
		this.lista = new ArrayList<String>();
		  try {
		      fw = new FileWriter(tiedosto=new File("sample.txt"));
		      bwriter  = new BufferedWriter(fw);
		    } catch (IOException e) {
		      System.out.println("An error occurred.");
		      e.printStackTrace();
		    }


		generoi(SIZE);
		tallenna();
	}

	public void generoi(int size) {

		String temp;

			for (int y=0;y<SIZE;y++) {
				switch(r.nextInt(2)) {
					case 0: {
						temp = generoiFloat();
					}
					break;
					case 1: {
						temp = generoiInt();
					}
					break;
					default: {
						temp ="";
					}

			}
			lista.add(temp);
		}
		char[] errors = generoiVirheet();
		for (int z=0;z<errors.length;z++) {
			lista.add(r.nextInt(lista.size()-1), String.valueOf(errors[r.nextInt(errors.length)]));
		}
	}

	public String generoiFloat() {
		float leftLimit = 1F,rightLimit = 10F,tempF;
		tempF = leftLimit + r.nextFloat() * (rightLimit - leftLimit);
		 String temp = String.valueOf(tempF) + DE[r.nextInt(DE.length)];
		 return temp;
	}
	public String generoiInt() {
		  return String.valueOf(r.nextInt(SIZE*2)+ DE[r.nextInt(DE.length)]);
	}


	public void tallenna() {

		for (int x=0;x<lista.size();x++) {
			try
			{


				bwriter.write((String) lista.get(x));
				bwriter.flush();
			}	catch (Exception e) {

			}
		}
		System.out.println("Tiedosto kirjoitettu!");

	}

	public char[] generoiVirheet() {
		char[] temp = new char[ERRORS];
		char err ='X';
		for (int x=0;x<temp.length;x++) {
			temp[x] = err;
		}
		return temp;
	}

	public void tulosta() {
		for (int x=0;x<lista.size();x++) {
			System.out.print(lista.get(x));
		}
	}

	public static void main(String[] args) {
		new Lukugeneraattori().tulosta();

	}

}

jalski [01.02.2022 21:41:45]

#

Selkeytin vähän koodia ja lisäsin jokusen kommentin. Oman versioni pitäsi nyt toimia oikein joka tilanteessa ja olla kohtuulisen nopea.

Jere, pistätkö jonnekin saataville kohtuullisen kokoisen generoimasi syötetiedoston ilman virheitä (minimissään vaikka joku 100000 lukua sisältävä)? Itsellä Raspberry PI 4B koneessa niin pieni sd-kortti, että en saa Javaa asennettua.

Jere Sumell [02.02.2022 07:33:38]

#

Tottahan toki pistän!

Uploadasin Dropboxiin 200 000 alkion sisältävän syötetiedoston, ja pistin tuon ERRORS -arvoksi 0, joten siinä ole virheitä.

Suora linkki, josta voit ladata itsellesi testiaineiston, on

https://www.dropbox.com/s/cfnfmzi97m05l7v/sample.txt?dl=0

Metabolix [02.02.2022 11:18:52]

#

Tietynlaiseen täsmällisen insinöörimäiseen käsittelyyn tilakone on ihan kätevä vaihtoehto. Tosin jalskin toteutus näyttää vaikeasti ylläpidettävältä, kun ilmeisesti tilasiirtymiä on laitettu koodiin taikalukuina. Tässä on tilakoneen toteutus PHP:llä, tilat ja siirtymät ovat yksinkertaisesti tekstinä.

<?php
function count_numbers($s) {
	$ends = ",; \t\n";
	$dfs = [
		"" => ["+-" => "sign", "0123456789" => "int", "." => "empty_float", " \t\n" => ""],
		"sign" => ["0123456789" => "int", "." => "empty_float"],
		"int" => ["0123456789" => "int", "." => "float", "e" => "e_empty", $ends => "int_done"],
		"empty_float" => ["0123456789" => "float"],
		"float" => ["0123456789" => "float", "e" => "e_empty", $ends => "float_done"],
		"e_empty" => ["+-" => "e_sign", "0123456789" => "e"],
		"e_sign" => ["0123456789" => "e"],
		"e" => ["0123456789" => "e", $ends => "float_done"],
	];

	// Ylläpidon helpottamiseksi yllä on esim. "+-" => "sign", ja tämä
	// muutetaan nyt koneellisesti muotoon "+" => "sign", "-" => "sign".
	foreach ($dfs as $i => $arr) {
		$dfs[$i] = [];
		foreach ($arr as $chars => $next) {
			foreach (str_split($chars) as $c) {
				$dfs[$i][$c] = $next;
			}
		}
	}

	$ints = $floats = 0;
	$line = $column = 0;
	$state = "";
	for ($i = 0; $i <= strlen($s); ++$i) {
		$c = $i < strlen($s) ? $s[$i] : "\n";
		if (!isset($dfs[$state][$c])) {
			$c_json = json_encode($c);
			$ok_json = json_encode(implode("", array_keys($dfs[$state])));
			return "error at $line:$column, found $c_json, expected one of $ok_json";
		}
		$column += 1;
		if ($c == "\n") {
			$line += 1;
			$column = 0;
		}
		$state = $dfs[$state][$c];
		if ($state == "float_done") {
			$floats += 1;
			$state = "";
		}
		if ($state == "int_done") {
			$ints += 1;
			$state = "";
		}
	}
	return "floats $floats, ints $ints";
}

Testikoodi yllä olevan käyttöön:

<?php
echo "data.txt: ", count_numbers(file_get_contents("data.txt")), "\n";
echo "syötä rivejä testattavaksi:\n";
while ($s = fgets(STDIN)) {
	echo count_numbers(trim($s)), "\n";
}

Tässä on vielä JS-koodi, jolla saa tehtyä deterministisen datatiedoston suoraan selaimella (F12:lla konsoli auki ja copy-paste sinne), ainoana satunnaistekijänä mahdolliset selainten JS-toteutusten laskutarkkuuden erot. Koodin lopussa oleva luku 1000000 on generoitavien lukuarvojen määrä.

(n => {
 let m = n / 10 |0;
 let signs = ["+", "-", ""], sign_i = 0;
 let ends = [" ", ",", ";", "\t", "\n"], end_i = 0;
 let data = Array(m).fill(0)
  .map((x,i) => 0.999834792348697614689 * i / m)
  .flatMap(x => [x, x/10, x/1000, 10*x, 10000*x, 1000000000 * x |0, 1e300 * x, 1e-300 * x, Math.pow(1e300, x), Math.pow(1e-300, x)])
  .map(x => signs[sign_i++ % signs.length] + x + ends[end_i++ % ends.length])
  .join("");

 let link = document.createElement("a");
 link.textContent = "data.txt @ " + new Date().toLocaleString();
 link.href = URL.createObjectURL(new File([data], "data.txt", {type: "text/plain"}));
 link.download = "data.txt";
 document.body.appendChild(link);
 link.click();
})(1000000);

Edellä oleva optimoimaton PHP-koodi käsittelee nämä miljoona lukua (20 megaa dataa) parissa sekunnissa omalla koneellani.

Generoidussa testidatassa ei ole virheitä, koska niiden laittaminen testidatan sekaan ei ole jalskin antamilla spekseillä järkevää, kun ilmeisesti käsittely loppuu ensimmäiseen virheeseen. Erilaiset virheet kannattaa laatia ja testata erikseen, järkevä testi ei ole vain selvästi väärän merkin heittäminen sekaan, vaan virhetyyppejä ovat esimerkiksi oikeiden merkkien sijainti väärässä kohti kuten "1+1", "+1.e1.2" jne.

jalskin speksit eivät ole aivan yksiselitteiset (eikä niitä jalskin koodistakaan helposti näe; jos tuo on selvää koodia, en palkkaa jalskia töihin). Esimerkiksi saako lukujen välillä olla useampi välilyönti (tai pilkku) ja miten sallivasti piste tulisi käsitellä (".1", "1.", "1.e+2"), ja mikä on kokonaisluvun määritelmä eli onko tällä jokin rajoitettu lukualue ja saako kokonaisluvun esittää e-notaatiolla (ja onko tällöin myös 1.3e1 kelvollinen kokonaisluku, sehän on sama kuin 13).

Lisäksi kun tehtävä lähti toisen keskustelun kysymyksestä, miten käyttäjän lukusyöte luetaan, niin tämähän on siihen liittymätön tehtävä, kun insinöörin suunnittelemat formaatit on kovakoodattu tilakoneeseen, lukuihin kelpaavat vain ASCII-merkit ja esimerkiksi pilkku on koodattu erottamaan lukuja, vaikka suomen kielessä pitäisi käyttää nimenomaan desimaalipilkkua. Mutta ei siitä enempää tässä keskustelussa.

Jere Sumell [02.02.2022 14:31:16]

#

Huomasin muuten vasta nyt tänään iltapäivemmällä, kun aamulla jaoin tuon Dropbox-jaon tänne, että tuosta alkuperäisestä lähdekoodistani puuttuu noista erotinmerkeistä välilyönti. No, sama kai se, ei sen puuttuminen nyt mikään suuri synti ole, jos joku tykkää generoida itse käyttäen tuota koodia, niin helposti voinee lisätä tuon välilyönti-merrkijonon alkion tuohon DE-taulukkoon, eikä koodissa tarvitse muutta muutoin mitään, niin sitten osa noista alkioiden erotinerkeistä on pelkkä välilyönti, kun ajaa ohjelman.

Tässä on nyt tuo tilakone kahdella kielellä esiteltynä tyylikkäästi, ja tuo tilakone itselleni vähän vieraampi, tiedä oikein pitäisi saada jotain lähdemateriaalivinkkiä, ja jotain täkyä sen suuntaan, miten sitä asiaa kannattaisia lähteä lähestymään, kun kiinnostus ottaa selvää on tilakoneesta.

Tämä loppuiltapäivä itselläni aika seesteinen, eikä mitään erityistä menoa tai ohjelmaa ole suunniteltuna, niin ajan kuluksi voisi Javalla olioperusteisen sellaisen tiedostoluku-ohjelman kirjoittaa, joka lukee tavu kerrallaan tai rivi kerrallaan tuota syötetiedostoa, ja summaa luvut ja antaa floattina lopputuloksen. En tiedä, ei se varmaankaan noopeudessa pärjää teidän noille esityksille, mutta voihan sen ihan ajan kuluksi ohjelmoida, vaikka varmaan mitään merkittävää hyötyä siinä nyt ei saavuta ketään.

Kävi ensin mielessä siitä tiedostonlukijasta Javalla, vaikka tosiaan en tiedä, saako millään nykyisellä korkean tason kielellä mitään aikaetua, ei varmaan saa millään ratkaisulla, mutta kun nuo erotinmerkit ovat määritelty ja tiedossa ennallaan, niin siinä voisi jotain metodin ylikuormitusta harkita ja sitten haarautuminen oikean metodin kutsuun kone osaisi päättää itse, mutta eihän se toimikaan, kun kaikki nuo ovat String-tyyppisiä, niin valintarakenteen kautta sitten StringTokenizer -ilmentymän mukan varmaan järkevintä se lukujen erottelu.

Kyllä varmaan nopeampi ohjelma tulee, kun lukee ensin rivi kerrallaan, kun taas jos lukisi merkki kerrallaan, niin sitten kuluisi aikaa siihen tiedon yhteenliittämiseen esim. StringBufferilla. Tietty molemmat voisi ohjelmoida, ja joku voi sitten mitata aikaa kukin tahollaan, kumpi suoriutuu nopeammin tehtävästä.

Varmaan nopeiten jollain kehittyneemmällä assemblerilla konekielisenä saa nopeimman ratkaisun aikaan, mutta katsellaan illan päälle, jos julkaisen Java-koodia lisää.

Jere Sumell [02.02.2022 15:00:08]

#

Tällaisen ohjelmoin, mutta jostain syystä ohjelma tulostaa aina summaksi 0.0 ikäänkuin se ei laskisi mitään. Tuossa Float.parseFloat(Object) -rivissä voi olla mätää, tai sitten pitäisi silmukan ulkopuolella enimmäinen StringTokenizer elementti laskea, nyt tässä on vikaa jotenkin todella paljon, kun ei summaa millään yhtään mitään.

import java.util.ArrayList;
import java.util.StringTokenizer;
import java.io.*;

public class Lukunopeus {

	private File tiedosto;
	private FileReader fr;
	private BufferedReader bwReader;
	private ArrayList<String> lista;
	private float summa;

	private final String DE = new String(", ,;,\t\n");



	public Lukunopeus() {
		try {
			tiedosto = new File("./Sample.txt");
			bwReader = new BufferedReader(new FileReader(tiedosto));
		}
		catch (Exception e) {
			      System.out.println("An error occurred.");
			      e.printStackTrace();
		}
		this.summa = 0;
		this.lista = new ArrayList<String>();
		this.lista = listaa();
		this.handleLista();

	}

	public ArrayList<String> listaa() {
		String temp ="";
		ArrayList<String> tempLista = new ArrayList<String>();
		try {
			while (fr.read() !=-1) {
				temp = (String) bwReader.readLine();
				tempLista.add(temp);
			}
		} catch (Exception e) {

		}
		return tempLista;
	}

	public void handleLista() {

		for (int x=0;x<lista.size()-1;x++) {
			this.summa+=handleMjono(lista.get(x));

		}

	}

	public float handleMjono(String mjono) {
		float summa = 0;
		StringTokenizer[] erottajat = new StringTokenizer[DE.length()];
		for (int x=0;x<DE.length();x++) {
			erottajat[x] = new StringTokenizer(mjono, String.valueOf(DE.charAt(x)));
		}
		StringTokenizer temp = null;
		for (int y=0;y<erottajat.length;y++) {
			temp = erottajat[y];
			while (temp.hasMoreElements()) {
				summa+=(float)Float.parseFloat((String)temp.nextElement());
			}
		}
		return summa;
	}



	public float getSumma() {
		return summa;
	}

	public void setSumma(float summa) {
		this.summa = summa;
	}

	public static void main(String[] args) {

		System.out.println("" +new Lukunopeus().getSumma());
	}
}

jalski [02.02.2022 15:36:54]

#

Metabolix kirjoitti:

Tosin jalskin toteutus näyttää vaikeasti ylläpidettävältä, kun ilmeisesti tilasiirtymiä on laitettu koodiin taikalukuina.

jalskin speksit eivät ole aivan yksiselitteiset (eikä niitä jalskin koodistakaan helposti näe; jos tuo on selvää koodia, en palkkaa jalskia töihin). Esimerkiksi saako lukujen välillä olla useampi välilyönti (tai pilkku) ja miten sallivasti piste tulisi käsitellä (".1", "1.", "1.e+2"), ja mikä on kokonaisluvun määritelmä eli onko tällä jokin rajoitettu lukualue ja saako kokonaisluvun esittää e-notaatiolla (ja onko tällöin myös 1.3e1 kelvollinen kokonaisluku, sehän on sama kuin 13).

Muutan nuo taulukot heksa muotoon, niin ajatus tulee selvemmin esille.

Ohjelmani skanneri ei välitä erotin merkeistä ollenkaan ( koodissa näkyy taulukossa pilkun ja puolipisteen käsittelyyn ' noop sana, eli "no operation" ). Skanneri myöskin skippaa "whitespace merkit".

Käytin numerosyötteen hyväksyntään kriteerinä sitä, että 8th pystyy muuntamaan siitä numeron. Nuo "ei valmiit" muodot mitkä hyväksytään on käsitelty lopuksi kun on päätellään luvun tyyppi. E-notaatiota käytettäessä luvusta tulee aina float-tyyppinen.

Jere Sumell [06.02.2022 21:35:47]

#

Jos joku osaisi auttaa tässä Java-koodissa, kun poistin tuon StringTokenizer -taulukon tuosta "Lukunopeus" -luokasta, jonka aiemmin esiin, ja yksinkertaistin Demo-luokkaan poistaen tuon tiedostosta luvun vielä tässä vaiheessa, ja yritän saada pelkästään tuota lukujen erottamista ja summaamista yhdestä listan alkiosta ulos. Tämä seuraava koodi jostain syystä ilmoittaa virheherjaa "String is empty", ajettaessa ohjelmaa, vaikka tarkistin Oraclen manuaaleista, että tyhjä merkkijono vastaa "" -eli nollaa pituutta ja on eri asia mitä null. Tuossa listan ensimmäisessä alkiossa on merkkijono "5;\t3.14", jonka lisään populoi-metodissa. Voiko joku selventää tämän, mahdollisesti auttaa korjaamaan lähdekoodissa olevan virheen, että saisi summattua nuo listan alkioiden luvut.

import java.util.ArrayList;
import java.util.StringTokenizer;

public class Demo {

	private float summa;
	private ArrayList<String> lista;

	public Demo() {
		this.summa = 0;
		this.lista = new ArrayList<String>();
		this.lista = this.populoi();
		this.handleLista();
	}

	public ArrayList<String> populoi() {
		ArrayList<String> tempLista = new ArrayList<String>();
		tempLista.add("5;\t3.14");
		tempLista.add("3.14,5");

		return tempLista;
	}


	public void handleLista() {

		for (int x=0;x<lista.size()-1;x++) {
			this.summa+=handleMjono(lista.get(x));

		}

	}

	public float handleMjono(String mjono) {
		StringBuffer sb = new StringBuffer("");
		float summa = 0;
		float value = 0;
		int start = 0;

			for (int y = start;y<mjono.length();y++) {
				sb.delete(0, sb.toString().length());
				if (mjono.charAt(y) == ',' || mjono.charAt(y) == ' ' || mjono.charAt(y) == ';' || mjono.charAt(y) == '\t' && mjono.length() != 0) {
					sb.append(mjono.substring(start, y-1));
					value = Float.valueOf(sb.toString());
					summa+=value;
					}
				continue;

			}

		return summa;
	}

	public float getSumma() {
		return summa;
	}

	public void setSumma(float summa) {
		this.summa = summa;
	}

	public static void main(String[] args) {
		Demo demo = new Demo();
		System.out.println(demo.getSumma());
	}
}

Tässä Konsolin virheilmoitus, kun yrittää ajaa ohjelmaa:

Exception in thread "main" java.lang.NumberFormatException: empty String
	at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
	at java.base/jdk.internal.math.FloatingDecimal.parseFloat(FloatingDecimal.java:122)
	at java.base/java.lang.Float.parseFloat(Float.java:461)
	at java.base/java.lang.Float.valueOf(Float.java:425)
	at Demo.handleMjono(Demo.java:45)
	at Demo.handleLista(Demo.java:29)
	at Demo.<init>(Demo.java:14)
	at Demo.main(Demo.java:64)

Tuo lienee lähempänä oikeaa tapaa erotella luvut tuo ehtolohkossa tarkistamisen jälkeen jokainen OR -operaattorilla noista erotinmerkeistä, koska ne voi olla sekaisin mikä tahansa voi esiintyä samalla rivillä. Tuota ihmettelen, kun AND -operaattorilla vielä toinen ehto, jonka JA tulisi täyttyä on tuo, että merkkijono ei ole 0 -merkkiä pitkä, eli empty string. Silti tulee tuo virheherja, että merkkijono on tyhjä.

mpni [07.02.2022 23:20:49]

#

*Ohjelmointihaaste

jalski [08.02.2022 07:57:07]

#

mpni kirjoitti:

*Ohjelmointihaaste

Jos tuntuu liian helpolta, niin voit vaikka kirjoitella tavukoodia käyttävän yksinkertaisen ohjelmointikielen tulkin Pascal tyyppiselle ohjelmointikielelle. Pitäisi onnistua muutamassa illassa...


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta