Slide 1

Slide 1 text

Λ׆༻ͯ͠ Ϟσϧ໊มߋͷ ෛ࠴ղফΛͨ͠࿩ @alpaca-tc ,BJHJPO3BJMT @alpaca_tc 5SBDF1PJOU

Slide 2

Slide 2 text

%FWFMPQFS1SPEVDUJWJUZ&OHJOFFS!4NBSU)3 ΞϧύΧୂ௕ # 最近のRailsコントリビュート # DB制約のチェックをtransaction末尾まで遅延可能 add_exclusion_constraint(…, deferrable: :deferred) add_unique_constraint(…, deferrable: :deferred)

Slide 3

Slide 3 text

ٕज़తෛ࠴

Slide 4

Slide 4 text

ٕज़తෛ࠴ Өڹൣғ͕େ͖͍ ࡞ۀྔ͕๲େ มߋͷ෭࡞༻ͷௐ͕ࠪେม Ͳ͔͜ΒखΛ͚ͭΕ͹ʜ

Slide 5

Slide 5 text

5SBDF1PJOU 3VCZඪ४ϥΠϒϥϦ ͞·͟·ͳΠϕϯτΛτϨʔε

Slide 6

Slide 6 text

ͳͥࢲ͸ࠓ೔ൃදΛ͢Δͷ͔ w ѱ໋໊͍نଇͩͬͨͨΊɺ͢΂ͯͷϞσϧ໊Λมߋ͢Δෛ࠴ղফ w DPNNJU ߦ w Өڹൣғ͸ΞϓϦέʔγϣϯશମ w 5SBDF1PJOU ΠϕϯτΛτϨʔε͢Δඪ४ϥΠϒϥϦ Λ׆༻ͯ͠׬਱ w ࣄྫ͕গͳ͍ͷͰɺօ༷ʹ໾ཱͭώϯτΛൃ৴Ͱ͖Δ͔΋

Slide 7

Slide 7 text

෼ͰΈͳ͞·͕ಘΒΕΔ͜ͱ w 5SBDF1PJOUͷجૅ஌ࣝ w 5SBDF1PJOUΛ׆༻͢ΔͨΊͷ5JQT w ॳڃऀ w 5SBDF1PJOU͕໘ന͍ w தڃऀҎ্ w ෛ࠴ղফʹ໾ཱͭώϯτ

Slide 8

Slide 8 text

ΞδΣϯμ w Ϟσϧ໊Λมߋ͢Δෛ࠴ղফͰ΍Γ͍ͨ͜ͱ w 5SBDF1PJOUͷجຊ w 5SBDF1PJOUΛ׆༻͢ΔͨΊͷ5JQT w σϞ w ·ͱΊ

Slide 9

Slide 9 text

Ϟσϧ໊Λมߋ͢Δ ෛ࠴ղফͰ΍Γ͍ͨ͜ͱ

Slide 10

Slide 10 text

1SPKFDU4IFFU4FDUJPO*UFN*OQVU/VNCFS w ωʔϜεϖʔεͰωετͨ͠Ϟσϧ໊Λ࠾༻͍ͯͨ͠ w ʮઃఆΑΓن໿ʯʹଇΒͣɺؔ࿈ఆ͕ٛ৑௕ w ςʔϒϧ໊ɾΧϥϜ໊ɾؔ࿈໊͕௕͘ͳΓ͕ͪ w ΧϥϜ໊͕௕͗ͯ͢3BJMTͷόάΛ౿ΉSBJMTSBJMT

Slide 11

Slide 11 text

1SPKFDU4IFFU4FDUJPO*UFN*OQVU/VNCFS w ϞσϧΛBQQNPEFMT௚Լʹ഑ஔ

Slide 12

Slide 12 text

1SPKFDU4IFFU4FDUJPO*UFN*OQVU/VNCFS w ϞσϧΛBQQNPEFMT௚Լʹ഑ஔ w มߋ͕ඞཁͳ΋ͷ w ఆ਺ Ϟσϧ໊ ɺϑΝΠϧύε w ؔ࿈໊ͷϝλϓϩDSFBUF@YYY CVJME@YYY w ΧϥϜ໊YYY@JE w 'BDUPSZ#PUɺ9YY%FDPSBUPSɺ"3ϝιου܈XIFSFKPJOTʜ

Slide 13

Slide 13 text

ͱͯ΋ख࡞ۀͰ͸ஔ׵Ͱ͖ͳ͍ʂ

Slide 14

Slide 14 text

5SBDF1PJOU ର৅ ར༻ՕॴΛಛఆ ஔ׵ॲཧ w ఆ਺໊ w ؔ࿈໊ w ΧϥϜ໊ T0ME/FXH GJMFQBUIUPBSC- GJMFQBUIUPCSC- GJMFQBUIUPD@TQFDSC-

Slide 15

Slide 15 text

5SBDF1PJOUͷجຊ

Slide 16

Slide 16 text

5SBDF1PJOUͷجຊ w ඪ४ϥΠϒϥϦ w 3VCZ্ͷ༷ʑͳΠϕϯτΛϑοΫͰ͖Δ w DBMM3VCZͰهड़͞Εͨϝιουͷݺͼग़͠ w MJOFࣜͷධՁ w SFUVSO3VCZͰهड़͞Εͨϝιουݺͼग़͔͠ΒͷϦλʔϯ w SBJTFྫ֎ͷൃੜ w ʜଞଟ਺

Slide 17

Slide 17 text

trace_point = TracePoint.new(:call) do |tp| pp([tp.event, tp.method_id, tp.path, tp.defined_class]) end def hello "hello" end def say_hello hello end trace_point.enable say_hello

Slide 18

Slide 18 text

trace_point = TracePoint.new(:call) do |tp| pp([tp.event, tp.method_id, tp.path, tp.defined_class]) end def hello "hello" end def say_hello hello end trace_point.enable say_hello ΠϕϯτͷτϨʔεΛ։࢝

Slide 19

Slide 19 text

trace_point = TracePoint.new(:call) do |tp| pp([tp.event, tp.method_id, tp.path, tp.defined_class]) end def hello "hello" end def say_hello hello end trace_point.enable say_hello ϝιουݺͼग़͠

Slide 20

Slide 20 text

trace_point = TracePoint.new(:call) do |tp| pp([tp.event, tp.method_id, tp.path, tp.defined_class]) end def hello "hello" end def say_hello hello end trace_point.enable say_hello ϒϩοΫ͕࣮ߦ #=> [:call, :say_hello, "/private/tmp/xxx.rb", Object]

Slide 21

Slide 21 text

trace_point = TracePoint.new(:call) do |tp| pp([tp.event, tp.method_id, tp.path, tp.defined_class]) end def hello "hello" end def say_hello hello end trace_point.enable say_hello #=> [:call, :say_hello, "/private/tmp/xxx.rb", Object] ϝιουݺͼग़͠

Slide 22

Slide 22 text

trace_point = TracePoint.new(:call) do |tp| pp([tp.event, tp.method_id, tp.path, tp.defined_class]) end def hello "hello" end def say_hello hello end trace_point.enable say_hello ϒϩοΫ͕࣮ߦ #=> [:call, :say_hello, "/private/tmp/xxx.rb", Object] #=> [:call, :hello, "/private/tmp/xxx.rb", Object]

Slide 23

Slide 23 text

trace_point = TracePoint.new(:call) do |tp| pp([tp.event, tp.method_id, tp.path, tp.defined_class]) end def hello "hello" end def say_hello hello end trace_point.enable say_hello w EF fi OFE@DMBTTϝιουΛఆٛͨ͠Ϋϥε͔Ϟδϡʔϧ w TFMGΠϕϯτΛൃੜͤͨ͞ΦϒδΣΫτ w NFUIPE@JEϝιουͷఆٛ࣌ͷ໊લ w QBSNFUFSTϝιουͷύϥϝʔλఆٛ w CJOEJOHϝιουͷCJOEJOHɻFWBMΛ࢖͑͹ͳΜͰ΋Ͱ͖Δ

Slide 24

Slide 24 text

5SBDF1PJOUΛ ׆༻͢ΔͨΊͷ5JQT

Slide 25

Slide 25 text

5SBDF1PJOUΛ׆༻͢ΔͨΊͷ5JQT w ϝιουͷݺͼग़͠ϑΝΠϧͷಛఆ w ϝιουΛݺͼग़ͨ͠ιʔείʔυͷಛఆ w 5SBDF1PJOUͷ࠷దԽ w ύϥϝʔλ஋ͷऔಘ

Slide 26

Slide 26 text

ϝιουͷݺͼग़͠ϑΝΠϧͷಛఆ 5ISFBEFBDI@DBMMFS@MPDBUJPO w BQQTQFD഑Լͷஔ׵ର৅ͷϑΝΠϧΛಛఆ͍ͨ͠ w DBMMFSܥͷελοΫΛར༻͢Δ w 3VCZ͔Β͸5ISFBEFBDI@DBMMFS@MPDBUJPO͕଎͍ ͜Μͳ஋ͷ഑ྻ͕औΕΔ

Slide 27

Slide 27 text

# 今回の置換では、appかspec配下にあるファイルが対象 TARGETS = [ Rails.root.join('app').to_s, Rails.root.join('spec').to_s ].freeze # @return [Thread::Backtrace::Location] ファイルパスや行数情報 def find_caller_location Thread.each_caller_location do |location| path = location.path next if path == __FILE__ return location if TARGETS.any? { path.start_with?(_1) } end end #=> "app/models/input_number.rb:11:in `tenant'" location = find_caller_location

Slide 28

Slide 28 text

# 今回の置換では、appかspec配下にあるファイルが対象 TARGETS = [ Rails.root.join('app').to_s, Rails.root.join('spec').to_s ].freeze # @return [Thread::Backtrace::Location] ファイルパスや行数情報 def find_caller_location Thread.each_caller_location do |location| path = location.path next if path == __FILE__ return location if TARGETS.any? { path.start_with?(_1) } end end #=> "app/models/input_number.rb:11:in `tenant'" location = find_caller_location ஔ׵ର৅Λࢦఆ

Slide 29

Slide 29 text

# 今回の置換では、appかspec配下にあるファイルが対象 TARGETS = [ Rails.root.join('app').to_s, Rails.root.join('spec').to_s ].freeze # @return [Thread::Backtrace::Location] ファイルパスや行数情報 def find_caller_location Thread.each_caller_location do |location| path = location.path next if path == __FILE__ return location if TARGETS.any? { path.start_with?(_1) } end end #=> "app/models/input_number.rb:11:in `tenant'" location = find_caller_location ελοΫΛॱʹḪΔ

Slide 30

Slide 30 text

# 今回の置換では、appかspec配下にあるファイルが対象 TARGETS = [ Rails.root.join('app').to_s, Rails.root.join('spec').to_s ].freeze # @return [Thread::Backtrace::Location] ファイルパスや行数情報 def find_caller_location Thread.each_caller_location do |location| path = location.path next if path == __FILE__ return location if TARGETS.any? { path.start_with?(_1) } end end #=> "app/models/input_number.rb:11:in `tenant'" location = find_caller_location ஔ׵ର৅ͳΒ஋Λฦ͢

Slide 31

Slide 31 text

# 今回の置換では、appかspec配下にあるファイルが対象 TARGETS = [ Rails.root.join('app').to_s, Rails.root.join('spec').to_s ].freeze # @return [Thread::Backtrace::Location] ファイルパスや行数情報 def find_caller_location Thread.each_caller_location do |location| path = location.path next if path == __FILE__ return location if TARGETS.any? { path.start_with?(_1) } end end #=> "app/models/input_number.rb:11:in `tenant'" location = find_caller_location ύεͱߦ͕Θ͔Δ

Slide 32

Slide 32 text

ϝιουΛݺͼग़ͨ͠ιʔείʔυͷಛఆ 5ISFBE#BDLUSBDF-PDBUJPOΑΓਖ਼֬ͳҐஔ৘ใ w 5ISFBE#BDLUSBDF-PDBUJPO͸ϑΝΠϧύε΍ߦ਺·Ͱ w ϑΝΠϧɾߦ਺ΑΓ΋ਫ਼៛ͳҐஔ৘ใΛಛఆͯ͠ɺஔ׵Λָʹ͍ͨ͠ w 3VCZ7."CTUSBDU4ZOUBY5SFFͰɺΑΓਖ਼֬ͳίʔυҐஔΛಛఆ͢Δ

Slide 33

Slide 33 text

class Hello def hello puts "hello" end end 3VCZ7."CTUSBDU4ZOUBY5SFFQBSTF SCOPE@#13 CLASS@#12 COLON2(:Hello)@#0 SCOPE@#11 BLOCK@#9 BEGIN@#1 DEFN(:hello)@#3 SCOPE@#8 ARGS@#4 FCALL(puts)@#5 LIST@#7 STR(hello)@#6 ιʔείʔυ ந৅ߏจ໦ "45

Slide 34

Slide 34 text

class Hello def hello puts "hello" end end SCOPE@#13 CLASS@#12 COLON2(:Hello)@#0 SCOPE@#11 BLOCK@#9 BEGIN@#1 DEFN(:hello)@#3 SCOPE@#8 ARGS@#4 FCALL(puts)@#5 LIST@#7 STR(hello)@#6 3VCZ7."CTUSBDU4ZOUBY5SFFQBSTF ֤ϊʔυ͸ϢχʔΫͳOPEF@JEΛ࣋ͭ ιʔείʔυ ந৅ߏจ໦ "45

Slide 35

Slide 35 text

ϝιουͷݺͼग़͠ιʔείʔυͷಛఆ 3VCZ7."CTUSBDU4ZOUBY5SFF w 3VCZ7."CTUSBDU4ZOUBY5SFFOPEF@JE@GPS@CBDLUSBDF@MPDBUJPO w 5ISFBE#BDLUSBDF-PDBUJPO͔ΒOPEF@JEΛऔಘͰ͖Δ w OPEF@JEͰ୳ࡧ͢Ε͹ɺݺͼग़͠Λͨ͠ιʔείʔυΛಛఆՄೳ w ࣮ݧతػೳ w ޙํޓ׵ੑ͸อূ͞Εͳ͍ w ͨ·ʹؒҧͬͨ݁ՌΛฦ͢ ମײͰ͙Β͍

Slide 36

Slide 36 text

# 呼び出し元のThread::Backtrace::Locationを取得 caller_location = find_caller_location # node_idを取得 node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location( caller_location ) # ファイルパス + node_idで正確なソースコードを取得できる ast = RubyVM::AbstractSyntaxTree.parse_file( caller_location.path, keep_script_lines: true ) node = find_node_id(ast, node_id) #=> 正確な位置情報やソースコードを取得できる [node.first_lineno, node.first_column, node.last_lineno, node.last_column] node.source

Slide 37

Slide 37 text

# 呼び出し元のThread::Backtrace::Locationを取得 caller_location = find_caller_location # node_idを取得 node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location( caller_location ) # ファイルパス + node_idで正確なソースコードを取得できる ast = RubyVM::AbstractSyntaxTree.parse_file( caller_location.path, keep_script_lines: true ) node = find_node_id(ast, node_id) #=> 正確な位置情報やソースコードを取得できる [node.first_lineno, node.first_column, node.last_lineno, node.last_column] node.source ݺͼग़͠ݩͷ৘ใΛऔಘ

Slide 38

Slide 38 text

# 呼び出し元のThread::Backtrace::Locationを取得 caller_location = find_caller_location # node_idを取得 node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location( caller_location ) # ファイルパス + node_idで正確なソースコードを取得できる ast = RubyVM::AbstractSyntaxTree.parse_file( caller_location.path, keep_script_lines: true ) node = find_node_id(ast, node_id) #=> 正確な位置情報やソースコードを取得できる [node.first_lineno, node.first_column, node.last_lineno, node.last_column] node.source OPEF@JEʹม׵

Slide 39

Slide 39 text

# 呼び出し元のThread::Backtrace::Locationを取得 caller_location = find_caller_location # node_idを取得 node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location( caller_location ) # ファイルパス + node_idで正確なソースコードを取得できる ast = RubyVM::AbstractSyntaxTree.parse_file( caller_location.path, keep_script_lines: true ) node = find_node_id(ast, node_id) #=> 正確な位置情報やソースコードを取得できる [node.first_lineno, node.first_column, node.last_lineno, node.last_column] node.source "45͔ΒҰக͢ΔOPEFΛऔಘ

Slide 40

Slide 40 text

# 呼び出し元のThread::Backtrace::Locationを取得 caller_location = find_caller_location # node_idを取得 node_id = RubyVM::AbstractSyntaxTree.node_id_for_backtrace_location( caller_location ) # ファイルパス + node_idで正確なソースコードを取得できる ast = RubyVM::AbstractSyntaxTree.parse_file( caller_location.path, keep_script_lines: true ) node = find_node_id(ast, node_id) #=> 正確な位置情報やソースコードを取得できる [node.first_lineno, node.first_column, node.last_lineno, node.last_column] node.source ιʔείʔυͷ಺༰

Slide 41

Slide 41 text

5SBDF1PJOUͷ࠷దԽ w ͜͜ͰΫΠζͰ͢ɻ

Slide 42

Slide 42 text

count = 0 TracePoint.new(:call) do count += 1 end.enable # この1行で、内部のメソッド呼び出しは何回起こる? User.where(id: 1).load puts count #=> ??? DPVOU͸͍ͭ͘ʹͳΔͰ͠ΐ͏͔

Slide 43

Slide 43 text

DPVOU͸͍ͭ͘ʹͳΔͰ͠ΐ͏͔ count = 0 TracePoint.new(:call) do count += 1 end.enable # この1行で、内部のメソッド呼び出しは何回起こる? User.where(id: 1).load puts count #=> 約5,000回

Slide 44

Slide 44 text

ૣظϦλʔϯ͢Δ ࠷దԽΛ͠·͠ΐ͏

Slide 45

Slide 45 text

TARGET_METHODS = [ ActiveRecord::Base.method(:has_one), ActiveRecord::Base.method(:has_many), ActiveRecord::Base.method(:belongs_to), ActiveRecord::Base.method(:has_and_belongs_to_many), ].map { [_1.owner, _1.name] }.to_set trace_point = TracePoint.new(:call) do |tp| next unless TARGET_METHODS.include?([tp.defined_class, tp_method_id]]) ... end ඞཁͳϝιουҎ֎͸ૣظϦλʔϯ

Slide 46

Slide 46 text

TARGET_METHODS = [ ActiveRecord::Base.method(:has_one), ActiveRecord::Base.method(:has_many), ActiveRecord::Base.method(:belongs_to), ActiveRecord::Base.method(:has_and_belongs_to_many), ].map { [_1.owner, _1.name] }.to_set trace_point = TracePoint.new(:call) do |tp| next unless TARGET_METHODS.include?([tp.defined_class, tp_method_id]]) ... end ඞཁͳϝιουҎ֎͸ૣظϦλʔϯ ؔ࿈ఆٛͷϝιουΛࢦఆ

Slide 47

Slide 47 text

TARGET_METHODS = [ ActiveRecord::Base.method(:has_one), ActiveRecord::Base.method(:has_many), ActiveRecord::Base.method(:belongs_to), ActiveRecord::Base.method(:has_and_belongs_to_many), ].map { [_1.owner, _1.name] }.to_set trace_point = TracePoint.new(:call) do |tp| next unless TARGET_METHODS.include?([tp.defined_class, tp_method_id]]) ... end ඞཁͳϝιουҎ֎͸ૣظϦλʔϯ τϨʔεର৅֎͸ૣظϦλʔϯ

Slide 48

Slide 48 text

ύϥϝʔλ஋ͷऔಘ w QSFMPBEKPJOTͳͲͷϝιου͸Ҿ਺ͷؔ࿈໊͕ஔ׵ର৅ͩͬͨ w 5SBDF1PJOUQBSBNFUFSTͰҾ਺ͷ໊લΛऔಘ w 5SBDF1PJOUCJOEJOHMPDBM@WBSJBCMF@HFUʹ౉͢

Slide 49

Slide 49 text

trace_point = TracePoint.new(:call) do |tp| names = tp.parameters.map { _2 } parameters = names.to_h do [_1, tp.binding.local_variable_get(_1)] end pp(parameters) end

Slide 50

Slide 50 text

trace_point = TracePoint.new(:call) do |tp| names = tp.parameters.map { _2 } parameters = names.to_h do [_1, tp.binding.local_variable_get(_1)] end pp(parameters) end ύϥϝʔλ໊Λऔಘ

Slide 51

Slide 51 text

trace_point = TracePoint.new(:call) do |tp| names = tp.parameters.map { _2 } parameters = names.to_h do [_1, tp.binding.local_variable_get(_1)] end pp(parameters) end ஋Λऔಘ

Slide 52

Slide 52 text

def a(*, **, &); end def b(...); end ແ໊Ҿ਺͸MPDBM@WBSJBCMF@HFUͰ͖ͳ͍ʂ

Slide 53

Slide 53 text

def extract_rest_parameters(*args, **options, &block) { :* => args, :** => options, :"..." => [args, options, block], :& => block } end trace_point = TracePoint.new(:call) do |tp| rest_names = tp.parameters.filter_map { _2 if [:*, :**, :&].include?(_2) } rest_variables = begin tp.binding.eval("extract_rest_parameters(#{rest_names.join(', ')})").slice(*rest_names) rescue SyntaxError tp.binding.eval("extract_rest_parameters(...)").slice(:"...") end pp rest_variables end trace_point.enable

Slide 54

Slide 54 text

def extract_rest_parameters(*args, **options, &block) { :* => args, :** => options, :"..." => [args, options, block], :& => block } end trace_point = TracePoint.new(:call) do |tp| rest_names = tp.parameters.filter_map { _2 if [:*, :**, :&].include?(_2) } rest_variables = begin tp.binding.eval("extract_rest_parameters(#{rest_names.join(', ')})").slice(*rest_names) rescue SyntaxError tp.binding.eval("extract_rest_parameters(...)").slice(:"...") end pp rest_variables end trace_point.enable ແ໊ͳSFTUҾ਺໊ΛऔΓग़͢

Slide 55

Slide 55 text

def extract_rest_parameters(*args, **options, &block) { :* => args, :** => options, :"..." => [args, options, block], :& => block } end trace_point = TracePoint.new(:call) do |tp| rest_names = tp.parameters.filter_map { _2 if [:*, :**, :&].include?(_2) } rest_variables = begin tp.binding.eval("extract_rest_parameters(#{rest_names.join(', ')})").slice(*rest_names) rescue SyntaxError tp.binding.eval("extract_rest_parameters(...)").slice(:"...") end pp rest_variables end trace_point.enable FYUSBDU@SFTU@QBSBNFUFST

Slide 56

Slide 56 text

def extract_rest_parameters(*args, **options, &block) { :* => args, :** => options, :"..." => [args, options, block], :& => block } end trace_point = TracePoint.new(:call) do |tp| rest_names = tp.parameters.filter_map { _2 if [:*, :**, :&].include?(_2) } rest_variables = begin tp.binding.eval("extract_rest_parameters(#{rest_names.join(', ')})").slice(*rest_names) rescue SyntaxError tp.binding.eval("extract_rest_parameters(...)").slice(:"...") end pp rest_variables end trace_point.enable ม਺ʹଋറ͞ΕΔ

Slide 57

Slide 57 text

def extract_rest_parameters(*args, **options, &block) { :* => args, :** => options, :"..." => [args, options, block], :& => block } end trace_point = TracePoint.new(:call) do |tp| rest_names = tp.parameters.filter_map { _2 if [:*, :**, :&].include?(_2) } rest_variables = begin tp.binding.eval("extract_rest_parameters(#{rest_names.join(', ')})").slice(*rest_names) rescue SyntaxError tp.binding.eval("extract_rest_parameters(...)").slice(:"...") end pp rest_variables end trace_point.enable ύϥϝʔλ໊Λऔಘ

Slide 58

Slide 58 text

def extract_rest_parameters(*args, **options, &block) { :* => args, :** => options, :"..." => [args, options, block], :& => block } end trace_point = TracePoint.new(:call) do |tp| rest_names = tp.parameters.filter_map { _2 if [:*, :**, :&].include?(_2) } rest_variables = begin tp.binding.eval("extract_rest_parameters(#{rest_names.join(', ')})").slice(*rest_names) rescue SyntaxError tp.binding.eval("extract_rest_parameters(...)").slice(:"...") end pp rest_variables end trace_point.enable ͷ৔߹͸ͪ͜Β͕ॲཧ͞ΕΔ

Slide 59

Slide 59 text

σϞ w 044NBTUPEPONBTUPEPO w ؔ࿈໊Λ࢖ͬͨϝιουݺͼग़͠ՕॴΛର৅ w CFMPOHT@UPVTFSͳΒ͹ w CVJME@VTFSDSFBUF@VTFSSFMPBE@VTFSVTFSVTFS

Slide 60

Slide 60 text

·ͱΊ w 5SBDF1PJOUΛ׆༻͢Δͱϝιουݺͼग़͠ͷ৘ใΛऔಘՄೳ w Өڹൣғௐࠪ΍ஔ׵ॲཧʹ׆͔ͤΔ w ஔ׵ॲཧ͸τϥϒϧͳ͘׬਱Ͱ͖ͨͷ͔ʁ w ʮػցతͳஔ׵ม਺ͳͲҰ෦ͷखಈஔ׵ʯͰ׬਱ w ςετΛॻ͍͍ͯͳ͔ͬͨՕॴͰΤϥʔ w ʮ4NBSU)3Ϟσϧ໊ʯͰݕࡧ͢ΔͱςοΫϒϩά͕ݟΕ·͢ w 5SBDF1PJOUͷ໘ന͞ɺෛ࠴ղফʹ׆͔ͤͦ͏ͳώϯτ͸఻ΘΓ·͔ͨ͠ʁ

Slide 61

Slide 61 text

͋Γ͕ͱ͏͍͟͝·ͨ͠